Dive planning for closed circuit rebreather

This misses a single issue to be used as a base for further discussion:
The CC setpoint is used for the next segment, not the one specified for. I
also have in mind to modify the existing code to use setpoints specified
in mbar and plain integer instead of float values.

Signed-off-by: Jan Schubert <Jan.Schubert@GMX.li>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
This commit is contained in:
Jan Schubert 2013-01-23 20:12:24 +01:00 committed by Dirk Hohndel
parent 37282176d5
commit 9df9fc088d
3 changed files with 67 additions and 14 deletions

3
dive.h
View file

@ -587,6 +587,7 @@ struct divedatapoint {
int depth; int depth;
int o2; int o2;
int he; int he;
int po2;
gboolean entered; gboolean entered;
struct divedatapoint *next; struct divedatapoint *next;
}; };
@ -600,7 +601,7 @@ struct diveplan {
}; };
void plan(struct diveplan *diveplan, char **cache_datap, struct dive **divep); void plan(struct diveplan *diveplan, char **cache_datap, struct dive **divep);
void plan_add_segment(struct diveplan *diveplan, int duration, int depth, int o2, int he); void plan_add_segment(struct diveplan *diveplan, int duration, int depth, int o2, int he, int po2);
void add_duration_to_nth_dp(struct diveplan *diveplan, int idx, int duration, gboolean is_rel); void add_duration_to_nth_dp(struct diveplan *diveplan, int idx, int duration, gboolean is_rel);
void add_depth_to_nth_dp(struct diveplan *diveplan, int idx, int depth); void add_depth_to_nth_dp(struct diveplan *diveplan, int idx, int depth);
void add_gas_to_nth_dp(struct diveplan *diveplan, int idx, int o2, int he); void add_gas_to_nth_dp(struct diveplan *diveplan, int idx, int o2, int he);

View file

@ -763,7 +763,7 @@ static int calculate_otu(struct dive *dive, struct divecomputer *dc)
struct sample *psample = sample - 1; struct sample *psample = sample - 1;
t = sample->time.seconds - psample->time.seconds; t = sample->time.seconds - psample->time.seconds;
if (sample->po2) { if (sample->po2) {
po2 = sample->po2; po2 = sample->po2 / 1000;
} else { } else {
int o2 = active_o2(dive, dc, sample->time); int o2 = active_o2(dive, dc, sample->time);
po2 = o2 / 1000.0 * depth_to_mbar(sample->depth.mm, dive) / 1000.0; po2 = o2 / 1000.0 * depth_to_mbar(sample->depth.mm, dive) / 1000.0;

View file

@ -112,7 +112,7 @@ double tissue_at_end(struct dive *dive, char **cached_datap)
for (j = t0; j < t1; j++) { for (j = t0; j < t1; j++) {
int depth = interpolate(lastdepth, sample->depth.mm, j - t0, t1 - t0); int depth = interpolate(lastdepth, sample->depth.mm, j - t0, t1 - t0);
tissue_tolerance = add_segment(depth_to_mbar(depth, dive) / 1000.0, tissue_tolerance = add_segment(depth_to_mbar(depth, dive) / 1000.0,
&dive->cylinder[gasidx].gasmix, 1, sample->po2, dive); &dive->cylinder[gasidx].gasmix, 1, sample->po2 / 1000, dive);
} }
psample = sample; psample = sample;
t0 = t1; t0 = t1;
@ -139,7 +139,7 @@ int time_at_last_depth(struct dive *dive, int next_stop, char **cached_data_p)
while (deco_allowed_depth(tissue_tolerance, surface_pressure, dive, 1) > next_stop) { while (deco_allowed_depth(tissue_tolerance, surface_pressure, dive, 1) > next_stop) {
wait++; wait++;
tissue_tolerance = add_segment(depth_to_mbar(depth, dive) / 1000.0, tissue_tolerance = add_segment(depth_to_mbar(depth, dive) / 1000.0,
&dive->cylinder[gasidx].gasmix, 1, sample->po2, dive); &dive->cylinder[gasidx].gasmix, 1, 1.1, dive);
} }
return wait; return wait;
} }
@ -197,6 +197,7 @@ struct dive *create_dive_from_plan(struct diveplan *diveplan)
add_gas(dive, oldo2, oldhe); add_gas(dive, oldo2, oldhe);
while (dp) { while (dp) {
int o2 = dp->o2, he = dp->he; int o2 = dp->o2, he = dp->he;
int po2 = dp->po2;
int time = dp->time; int time = dp->time;
int depth = dp->depth; int depth = dp->depth;
struct sample *sample; struct sample *sample;
@ -223,6 +224,7 @@ struct dive *create_dive_from_plan(struct diveplan *diveplan)
/* Create sample */ /* Create sample */
sample = prepare_sample(dc); sample = prepare_sample(dc);
sample->po2 = po2;
sample->time.seconds = time; sample->time.seconds = time;
sample->depth.mm = depth; sample->depth.mm = depth;
finish_sample(dc); finish_sample(dc);
@ -250,7 +252,7 @@ void free_dps(struct divedatapoint *dp)
} }
} }
struct divedatapoint *create_dp(int time_incr, int depth, int o2, int he) struct divedatapoint *create_dp(int time_incr, int depth, int o2, int he, int po2)
{ {
struct divedatapoint *dp; struct divedatapoint *dp;
@ -259,6 +261,7 @@ struct divedatapoint *create_dp(int time_incr, int depth, int o2, int he)
dp->depth = depth; dp->depth = depth;
dp->o2 = o2; dp->o2 = o2;
dp->he = he; dp->he = he;
dp->po2 = po2;
dp->entered = FALSE; dp->entered = FALSE;
dp->next = NULL; dp->next = NULL;
return dp; return dp;
@ -275,7 +278,7 @@ struct divedatapoint *get_nth_dp(struct diveplan *diveplan, int idx)
dp = dp->next; dp = dp->next;
} }
while (i++ <= idx) { while (i++ <= idx) {
*ldpp = dp = create_dp(0, 0, 0, 0); *ldpp = dp = create_dp(0, 0, 0, 0, 0);
ldpp = &((*ldpp)->next); ldpp = &((*ldpp)->next);
} }
return dp; return dp;
@ -309,6 +312,13 @@ void add_gas_to_nth_dp(struct diveplan *diveplan, int idx, int o2, int he)
dp->o2 = o2; dp->o2 = o2;
dp->he = he; dp->he = he;
} }
void add_po2_to_nth_dp(struct diveplan *diveplan, int idx, int po2)
{
struct divedatapoint *dp = get_nth_dp(diveplan, idx);
dp->po2 = po2;
}
void add_to_end_of_diveplan(struct diveplan *diveplan, struct divedatapoint *dp) void add_to_end_of_diveplan(struct diveplan *diveplan, struct divedatapoint *dp)
{ {
struct divedatapoint **lastdp = &diveplan->dp; struct divedatapoint **lastdp = &diveplan->dp;
@ -325,9 +335,9 @@ void add_to_end_of_diveplan(struct diveplan *diveplan, struct divedatapoint *dp)
dp->time += lasttime; dp->time += lasttime;
} }
void plan_add_segment(struct diveplan *diveplan, int duration, int depth, int o2, int he) void plan_add_segment(struct diveplan *diveplan, int duration, int depth, int o2, int he, int po2)
{ {
struct divedatapoint *dp = create_dp(duration, depth, o2, he); struct divedatapoint *dp = create_dp(duration, depth, o2, he, po2);
add_to_end_of_diveplan(diveplan, dp); add_to_end_of_diveplan(diveplan, dp);
} }
@ -528,7 +538,7 @@ void plan(struct diveplan *diveplan, char **cached_datap, struct dive **divep)
{ {
struct dive *dive; struct dive *dive;
struct sample *sample; struct sample *sample;
int wait_time, o2, he; int wait_time, o2, he, po2;
int ceiling, depth, transitiontime; int ceiling, depth, transitiontime;
int stopidx, gi; int stopidx, gi;
double tissue_tolerance; double tissue_tolerance;
@ -551,6 +561,7 @@ void plan(struct diveplan *diveplan, char **cached_datap, struct dive **divep)
o2 = dive->cylinder[0].gasmix.o2.permille; o2 = dive->cylinder[0].gasmix.o2.permille;
he = dive->cylinder[0].gasmix.he.permille; he = dive->cylinder[0].gasmix.he.permille;
get_gas_from_events(&dive->dc, sample->time.seconds, &o2, &he); get_gas_from_events(&dive->dc, sample->time.seconds, &o2, &he);
po2 = dive->dc.sample[dive->dc.samples - 1].po2;
depth = dive->dc.sample[dive->dc.samples - 1].depth.mm; depth = dive->dc.sample[dive->dc.samples - 1].depth.mm;
tissue_tolerance = tissue_at_end(dive, cached_datap); tissue_tolerance = tissue_at_end(dive, cached_datap);
ceiling = deco_allowed_depth(tissue_tolerance, diveplan->surface_pressure / 1000.0, dive, 1); ceiling = deco_allowed_depth(tissue_tolerance, diveplan->surface_pressure / 1000.0, dive, 1);
@ -581,7 +592,7 @@ void plan(struct diveplan *diveplan, char **cached_datap, struct dive **divep)
#if DEBUG_PLAN & 2 #if DEBUG_PLAN & 2
printf("transitiontime %d:%02d to depth %5.2lfm\n", FRACTION(transitiontime, 60), stoplevels[stopidx] / 1000.0); printf("transitiontime %d:%02d to depth %5.2lfm\n", FRACTION(transitiontime, 60), stoplevels[stopidx] / 1000.0);
#endif #endif
plan_add_segment(diveplan, transitiontime, stoplevels[stopidx], o2, he); plan_add_segment(diveplan, transitiontime, stoplevels[stopidx], o2, he, po2);
/* re-create the dive */ /* re-create the dive */
delete_single_dive(dive_table.nr - 1); delete_single_dive(dive_table.nr - 1);
*divep = dive = create_dive_from_plan(diveplan); *divep = dive = create_dive_from_plan(diveplan);
@ -605,12 +616,12 @@ void plan(struct diveplan *diveplan, char **cached_datap, struct dive **divep)
printf("waittime %d:%02d at depth %5.2lfm\n", FRACTION(wait_time, 60), stoplevels[stopidx] / 1000.0); printf("waittime %d:%02d at depth %5.2lfm\n", FRACTION(wait_time, 60), stoplevels[stopidx] / 1000.0);
#endif #endif
if (wait_time) if (wait_time)
plan_add_segment(diveplan, wait_time, stoplevels[stopidx], o2, he); plan_add_segment(diveplan, wait_time, stoplevels[stopidx], o2, he, po2);
transitiontime = (stoplevels[stopidx] - stoplevels[stopidx - 1]) / 150; transitiontime = (stoplevels[stopidx] - stoplevels[stopidx - 1]) / 150;
#if DEBUG_PLAN & 2 #if DEBUG_PLAN & 2
printf("transitiontime %d:%02d to depth %5.2lfm\n", FRACTION(transitiontime, 60), stoplevels[stopidx - 1] / 1000.0); printf("transitiontime %d:%02d to depth %5.2lfm\n", FRACTION(transitiontime, 60), stoplevels[stopidx - 1] / 1000.0);
#endif #endif
plan_add_segment(diveplan, transitiontime, stoplevels[stopidx - 1], o2, he); plan_add_segment(diveplan, transitiontime, stoplevels[stopidx - 1], o2, he, po2);
/* re-create the dive */ /* re-create the dive */
delete_single_dive(dive_table.nr - 1); delete_single_dive(dive_table.nr - 1);
*divep = dive = create_dive_from_plan(diveplan); *divep = dive = create_dive_from_plan(diveplan);
@ -827,6 +838,29 @@ static int validate_depth(const char *text, int *mm_p)
return 1; return 1;
} }
static int validate_po2(const char *text, int *mbar_po2)
{
int po2;
if (!text)
return 0;
po2 = get_tenths(text, &text);
if (po2 < 0)
return 0;
while (isspace(*text))
text++;
while (isspace(*text))
text++;
if (*text)
return 0;
*mbar_po2 = po2 * 100;
return 1;
}
static int validate_volume(const char *text, int *sac) static int validate_volume(const char *text, int *sac)
{ {
int volume, imperial; int volume, imperial;
@ -880,7 +914,7 @@ static GtkWidget *add_entry_to_box(GtkWidget *box, const char *label)
} }
#define MAX_WAYPOINTS 8 #define MAX_WAYPOINTS 8
GtkWidget *entry_depth[MAX_WAYPOINTS], *entry_duration[MAX_WAYPOINTS], *entry_gas[MAX_WAYPOINTS]; GtkWidget *entry_depth[MAX_WAYPOINTS], *entry_duration[MAX_WAYPOINTS], *entry_gas[MAX_WAYPOINTS], *entry_po2[MAX_WAYPOINTS];
int nr_waypoints = 0; int nr_waypoints = 0;
static GtkListStore *gas_model = NULL; static GtkListStore *gas_model = NULL;
struct diveplan diveplan = {}; struct diveplan diveplan = {};
@ -980,6 +1014,19 @@ static gboolean duration_focus_out_cb(GtkWidget *entry, GdkEvent * event, gpoint
return FALSE; return FALSE;
} }
static gboolean po2_focus_out_cb(GtkWidget *entry, GdkEvent * event, gpointer data)
{
const char *po2text;
int po2;
int idx = data - NULL;
po2text = gtk_entry_get_text(GTK_ENTRY(entry));
if (validate_po2(po2text, &po2))
add_po2_to_nth_dp(&diveplan, idx, po2);
show_planned_dive();
return FALSE;
}
/* Subsurface follows the lead of most divecomputers to use times /* Subsurface follows the lead of most divecomputers to use times
* without timezone - so all times are implicitly assumed to be * without timezone - so all times are implicitly assumed to be
* local time of the dive location; so in order to give the current * local time of the dive location; so in order to give the current
@ -1077,15 +1124,19 @@ static void add_waypoint_widgets(GtkWidget *box, int idx)
entry_depth[idx] = add_entry_to_box(hbox, _("Ending Depth")); entry_depth[idx] = add_entry_to_box(hbox, _("Ending Depth"));
entry_duration[idx] = add_entry_to_box(hbox, _("Segment Time")); entry_duration[idx] = add_entry_to_box(hbox, _("Segment Time"));
entry_gas[idx] = add_gas_combobox_to_box(hbox, _("Gas Used"), idx); entry_gas[idx] = add_gas_combobox_to_box(hbox, _("Gas Used"), idx);
entry_po2[idx] = add_entry_to_box(hbox, _("CC SetPoint"));
} else { } else {
entry_depth[idx] = add_entry_to_box(hbox, NULL); entry_depth[idx] = add_entry_to_box(hbox, NULL);
entry_duration[idx] = add_entry_to_box(hbox, NULL); entry_duration[idx] = add_entry_to_box(hbox, NULL);
entry_gas[idx] = add_gas_combobox_to_box(hbox, NULL, idx); entry_gas[idx] = add_gas_combobox_to_box(hbox, NULL, idx);
entry_po2[idx] = add_entry_to_box(hbox, NULL);
} }
gtk_widget_add_events(entry_depth[idx], GDK_FOCUS_CHANGE_MASK); gtk_widget_add_events(entry_depth[idx], GDK_FOCUS_CHANGE_MASK);
g_signal_connect(entry_depth[idx], "focus-out-event", G_CALLBACK(depth_focus_out_cb), NULL + idx); g_signal_connect(entry_depth[idx], "focus-out-event", G_CALLBACK(depth_focus_out_cb), NULL + idx);
gtk_widget_add_events(entry_duration[idx], GDK_FOCUS_CHANGE_MASK); gtk_widget_add_events(entry_duration[idx], GDK_FOCUS_CHANGE_MASK);
g_signal_connect(entry_duration[idx], "focus-out-event", G_CALLBACK(duration_focus_out_cb), NULL + idx); g_signal_connect(entry_duration[idx], "focus-out-event", G_CALLBACK(duration_focus_out_cb), NULL + idx);
gtk_widget_add_events(entry_po2[idx], GDK_FOCUS_CHANGE_MASK);
g_signal_connect(entry_po2[idx], "focus-out-event", G_CALLBACK(po2_focus_out_cb), NULL + idx);
} }
static void add_waypoint_cb(GtkButton *button, gpointer _data) static void add_waypoint_cb(GtkButton *button, gpointer _data)
@ -1141,7 +1192,8 @@ void input_plan()
"An empty gas means 'use previous gas' (or AIR if no gas was specified).\n" "An empty gas means 'use previous gas' (or AIR if no gas was specified).\n"
"An entry that has a depth and a gas given but no time is special; it " "An entry that has a depth and a gas given but no time is special; it "
"informs the planner that the gas specified is available for the ascent " "informs the planner that the gas specified is available for the ascent "
"once the depth given has been reached.</small>")); "once the depth given has been reached.\n"
"CC SetPoint specifies CC dives, leave empty for OC.</small>"));
gtk_label_set_line_wrap(GTK_LABEL(label), TRUE); gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
gtk_label_set_use_markup(GTK_LABEL(label), TRUE); gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
gtk_box_pack_start(GTK_BOX(outervbox), label, TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(outervbox), label, TRUE, TRUE, 0);