From 3c31d0401dd9e4be2aa51e1bb2b72e0bc162cb9d Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Wed, 2 Jan 2013 21:21:36 -0800 Subject: First stab at deco calculations This seems to give us roughly the right data but needs a lot more testing. Signed-off-by: Dirk Hohndel --- Makefile | 5 ++- deco.c | 143 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ dive.h | 3 ++ profile.c | 27 ++++++++++++ 4 files changed, 177 insertions(+), 1 deletion(-) create mode 100644 deco.c diff --git a/Makefile b/Makefile index 81e4014fd..dc3a66aa8 100644 --- a/Makefile +++ b/Makefile @@ -130,7 +130,7 @@ LIBS = $(LIBXML2) $(LIBXSLT) $(LIBGTK) $(LIBGCONF2) $(LIBDIVECOMPUTER) $(EXTRALI MSGLANGS=$(notdir $(wildcard po/*po)) MSGOBJS=$(addprefix share/locale/,$(MSGLANGS:.po=.UTF-8/LC_MESSAGES/subsurface.mo)) -OBJS = main.o dive.o time.o profile.o info.o equipment.o divelist.o \ +OBJS = main.o dive.o time.o profile.o info.o equipment.o divelist.o deco.o \ parse-xml.o save-xml.o libdivecomputer.o print.o uemis.o uemis-downloader.o \ gtk-gui.o statistics.o file.o cochran.o $(OSSUPPORT).o $(RESFILE) @@ -244,6 +244,9 @@ divelist.o: divelist.c dive.h display.h divelist.h print.o: print.c dive.h display.h display-gtk.h $(CC) $(CFLAGS) $(GTK2CFLAGS) $(GLIB2CFLAGS) $(XML2CFLAGS) -c print.c +deco.o: deco.c dive.h + $(CC) $(CFLAGS) $(GLIB2CFLAGS) -c deco.c + libdivecomputer.o: libdivecomputer.c dive.h display.h display-gtk.h libdivecomputer.h $(CC) $(CFLAGS) $(GTK2CFLAGS) $(GLIB2CFLAGS) $(XML2CFLAGS) \ $(LIBDIVECOMPUTERCFLAGS) \ diff --git a/deco.c b/deco.c new file mode 100644 index 000000000..6901e6c6a --- /dev/null +++ b/deco.c @@ -0,0 +1,143 @@ +/* calculate deco values + * based on Bühlmann ZHL-16b + * based on an implemention by heinrichs weikamp for the DR5 + * the original file doesn't carry a license and is used here with + * the permission of Matthias Heinrichs + * + * The implementation below is (C) Dirk Hohndel 2012 and released under the GPLv2 + * + * clear_deco() - call to initialize for a new deco calculation + * add_segment(pressure, gasmix) - add 1 second at the given pressure, breathing gasmix + */ +#include "dive.h" + +//! Option structure for Buehlmann decompression. +struct buehlmann_config { + double satmult; //! safety at inert gas accumulation as percentage of effect (more than 100). + double desatmult; //! safety at inert gas depletion as percentage of effect (less than 100). + double safety_dist_deco_stop;//! assumed distance to official decompression where decompression takes places. + int last_deco_stop_in_mtr; //! depth of last_deco_stop. + double gf_high; //! gradient factor high (at surface). + double gf_low; //! gradient factor low (at bottom/start of deco calculation). + double gf_low_position_min; //! gf_low_position below surface_min_shallow. + double gf_low_position_max; //! gf_low_position below surface_max_depth. + double gf_high_emergency; //! emergency gf factors + double gf_low_emergency; //! gradient factor low (at bottom/start of deco calculation). +}; + +struct dive_data +{ + double pressure; //! pesent ambient pressure + double surface; //! pressure at water surface + struct gasmix *gasmix; //! current selected gas +}; + +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, + 0.2835, 0.261, 0.248, 0.2327}; + +const double buehlmann_N2_b[] = {0.5578, 0.6514, 0.7222, 0.7825, + 0.8126, 0.8434, 0.8693, 0.8910, + 0.9092, 0.9222, 0.9319, 0.9403, + 0.9477, 0.9544, 0.9602, 0.9653}; + +const double buehlmann_N2_t_halflife[] = {5.0, 8.0, 12.5, 18.5, + 27.0, 38.3, 54.3, 77.0, + 109.0, 146.0, 187.0, 239.0, + 305.0, 390.0, 498.0, 635.0}; + +const double buehlmann_N2_factor_expositon_one_second[] = { + 2.30782347297664E-003, 1.44301447809736E-003, 9.23769302935806E-004, 6.24261986779007E-004, + 4.27777107246730E-004, 3.01585140931371E-004, 2.12729727268379E-004, 1.50020603047807E-004, + 1.05980191127841E-004, 7.91232600646508E-005, 6.17759153688224E-005, 4.83354552742732E-005, + 3.78761777920511E-005, 2.96212356654113E-005, 2.31974277413727E-005, 1.81926738960225E-005}; + +const double buehlmann_He_a[] = { 1.6189, 1.383 , 1.1919, 1.0458, + 0.922 , 0.8205, 0.7305, 0.6502, + 0.595 , 0.5545, 0.5333, 0.5189, + 0.5181, 0.5176, 0.5172, 0.5119}; + +const double buehlmann_He_b[] = {0.4770, 0.5747, 0.6527, 0.7223, + 0.7582, 0.7957, 0.8279, 0.8553, + 0.8757, 0.8903, 0.8997, 0.9073, + 0.9122, 0.9171, 0.9217, 0.9267}; + +const double buehlmann_He_t_halflife[] = {1.88, 3.02, 4.72, 6.99, + 10.21, 14.48, 20.53, 29.11, + 41.20, 55.19, 70.69, 90.34, + 115.29, 147.42, 188.24, 240.03}; + +const double buehlmann_He_factor_expositon_one_second[] = { + 6.12608039419837E-003, 3.81800836683133E-003, 2.44456078654209E-003, 1.65134647076792E-003, + 1.13084424730725E-003, 7.97503165599123E-004, 5.62552521860549E-004, 3.96776399429366E-004, + 2.80360036664540E-004, 2.09299583354805E-004, 1.63410794820518E-004, 1.27869320250551E-004, + 1.00198406028040E-004, 7.83611475491108E-005, 6.13689891868496E-005, 4.81280465299827E-005}; + +#define WV_PRESSURE 0.0627 /* water vapor pressure */ + +double tissue_n2_sat[16]; +double tissue_he_sat[16]; +double tissue_tolerated_ambient_pressure[16]; +int ci_pointing_to_guiding_tissue; +int divetime; + +struct buehlmann_config buehlmann_config = { 1.0, 1.01, 0.5, 3, 95.0, 95.0, 10.0, 30.0, 95.0, 95.0 }; + +static double tissue_tolerance_calc(void) +{ + int ci = -1; + double tissue_inertgas_saturation, buehlmann_inertgas_a, buehlmann_inertgas_b; + double ret_tolerance_limit_ambient_pressure = -1.0; + + for (ci = 0; ci < 16; ci++) + { + tissue_inertgas_saturation = tissue_n2_sat[ci] + tissue_he_sat[ci]; + buehlmann_inertgas_a = ((buehlmann_N2_a[ci] * tissue_n2_sat[ci]) + (buehlmann_He_a[ci] * tissue_he_sat[ci])) / tissue_inertgas_saturation; + buehlmann_inertgas_b = ((buehlmann_N2_b[ci] * tissue_n2_sat[ci]) + (buehlmann_He_b[ci] * tissue_he_sat[ci])) / tissue_inertgas_saturation; + + tissue_tolerated_ambient_pressure[ci] = (tissue_inertgas_saturation - buehlmann_inertgas_a) * buehlmann_inertgas_b; + + if (tissue_tolerated_ambient_pressure[ci] > ret_tolerance_limit_ambient_pressure) + { + ci_pointing_to_guiding_tissue = ci; + ret_tolerance_limit_ambient_pressure = tissue_tolerated_ambient_pressure[ci]; + } + } + printf("%d:%02u %lf\n",FRACTION(divetime, 60), ret_tolerance_limit_ambient_pressure); + return (ret_tolerance_limit_ambient_pressure); +} + +/* add a second at the given pressure and gas to the deco calculation */ +double add_segment(double pressure, struct gasmix *gasmix) +{ + int ci; + double ppn2 = (pressure - WV_PRESSURE) * (1000 - gasmix->o2.permille - gasmix->he.permille) / 1000.0; + double pphe = (pressure - WV_PRESSURE) * gasmix->he.permille / 1000.0; + + divetime++; + printf("%2d:%02u N2 %2.3lf He %2.3lf",FRACTION(divetime, 60), ppn2, pphe); + /* right now we just do OC */ + for (ci = 0; ci < 16; ci++) { + if (ppn2 - tissue_n2_sat[ci] > 0) + tissue_n2_sat[ci] += buehlmann_config.satmult * (ppn2 - tissue_n2_sat[ci]) * buehlmann_N2_factor_expositon_one_second[ci]; + else + tissue_n2_sat[ci] += buehlmann_config.desatmult * (ppn2 - tissue_n2_sat[ci]) * buehlmann_N2_factor_expositon_one_second[ci]; + if (pphe - tissue_he_sat[ci] > 0) + tissue_he_sat[ci] += buehlmann_config.satmult * (pphe - tissue_he_sat[ci]) * buehlmann_He_factor_expositon_one_second[ci]; + else + tissue_he_sat[ci] += buehlmann_config.desatmult * (pphe - tissue_he_sat[ci]) * buehlmann_He_factor_expositon_one_second[ci]; + } + return tissue_tolerance_calc(); +} + +void clear_deco() +{ + int ci; + for (ci = 0; ci < 16; ci++) { + tissue_n2_sat[ci] = 0.0; + tissue_he_sat[ci] = 0.0; + tissue_tolerated_ambient_pressure[ci] = 0.0; + } + divetime = 0; +} diff --git a/dive.h b/dive.h index 9b92baaa7..b5d3368a5 100644 --- a/dive.h +++ b/dive.h @@ -572,6 +572,9 @@ extern void subsurface_command_line_exit(gint *, gchar ***); #define FRACTION(n,x) ((unsigned)(n)/(x)),((unsigned)(n)%(x)) +extern double add_segment(double pressure, struct gasmix *gasmix); +extern void clear_deco(void); + #ifdef DEBUGFILE extern char *debugfilename; extern FILE *debugfile; diff --git a/profile.c b/profile.c index dea1ad6ee..88f2cb80e 100644 --- a/profile.c +++ b/profile.c @@ -40,6 +40,7 @@ struct plot_data { int temperature; /* Depth info */ int depth; + int ceiling; int ndl; int stoptime; int stopdepth; @@ -1539,10 +1540,13 @@ static struct plot_info *create_plot_info(struct dive *dive, struct divecomputer struct plot_data *entry = NULL; struct event *ev; double amb_pressure, po2; + int surface_pressure = dive->surface_pressure.mbar ? dive->surface_pressure.mbar : 1013; /* The plot-info is embedded in the graphics context */ pi = &gc->pi; + /* reset deco information to start the calculation */ + clear_deco(); /* we want to potentially add synthetic plot_info elements for the gas changes */ nr = dc->samples + 4 + 2 * count_gas_change_events(dc); if (last_pi_entry) @@ -1724,6 +1728,24 @@ static struct plot_info *create_plot_info(struct dive *dive, struct divecomputer current->pressure_time += (entry->sec - (entry-1)->sec) * depth_to_mbar((entry->depth + (entry-1)->depth) / 2, dive) / 1000.0; missing_pr |= !SENSOR_PRESSURE(entry); + /* and now let's try to do some deco calculations */ + if (i > 0) { + int j; + int t0 = (entry - 1)->sec; + int t1 = entry->sec; + float ceiling_pressure = 0; + for (j = t0; j < t1; j++) { + int depth = 0.5 + (entry - 1)->depth + (j - t0) * (entry->depth - (entry - 1)->depth) / (t1 - t0); + double min_pressure = add_segment(depth_to_mbar(depth, dive) / 1000.0, &dive->cylinder[cylinderindex].gasmix); + if (min_pressure > ceiling_pressure) + ceiling_pressure = min_pressure; + } + ceiling_pressure = ceiling_pressure * 1000.0 + 0.5; + if (ceiling_pressure > surface_pressure) + entry->ceiling = rel_mbar_to_depth(ceiling_pressure - surface_pressure, dive); + else + entry->ceiling = 0; + } } if (entry) @@ -1965,6 +1987,11 @@ static void plot_string(struct plot_data *entry, char *buf, size_t bufsize, memcpy(buf2, buf, bufsize); snprintf(buf, bufsize, "%s\nT:%.1f %s", buf2, tempvalue, temp_unit); } + if (entry->ceiling) { + depthvalue = get_depth_units(entry->ceiling, NULL, &depth_unit); + memcpy(buf2, buf, bufsize); + snprintf(buf, bufsize, "%s\nCalculated ceiling %.0f %s", buf2, depthvalue, depth_unit); + } if (entry->stopdepth) { depthvalue = get_depth_units(entry->stopdepth, NULL, &depth_unit); memcpy(buf2, buf, bufsize); -- cgit v1.2.3-70-g09d2 From aab67e2a5bb9c74fcc60af4ade0192d31263ebc7 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Wed, 2 Jan 2013 22:45:21 -0800 Subject: Add configurable visualization of calculated ceiling This is on top of the deco information reported by the dive computer (in a different color - currently ugly green). The user needs to enable this via the Tec page of the preferences. Signed-off-by: Dirk Hohndel --- color.h | 2 ++ display-gtk.h | 1 + gtk-gui.c | 10 +++++++++- profile.c | 22 +++++++++++++++++++++- 4 files changed, 33 insertions(+), 2 deletions(-) diff --git a/color.h b/color.h index 23065d306..5d88fb5f7 100644 --- a/color.h +++ b/color.h @@ -7,9 +7,11 @@ // Greens #define CAMARONE1 { 0.0, 0.4, 0.0, 1 } #define FUNGREEN1 { 0.0, 0.4, 0.2, 1 } +#define FUNGREEN1_HIGH_TRANS { 0.0, 0.4, 0.2, 0.25 } #define KILLARNEY1 { 0.2, 0.4, 0.2, 1 } #define APPLE1 { 0.2, 0.6, 0.2, 1 } #define APPLE1_MED_TRANS { 0.2, 0.6, 0.2, 0.5 } +#define APPLE1_HIGH_TRANS { 0.2, 0.6, 0.2, 0.25 } #define LIMENADE1 { 0.4, 0.8, 0.0, 1 } #define ATLANTIS1 { 0.4, 0.8, 0.2, 1 } #define ATLANTIS2 { 0.6, 0.8, 0.2, 1 } diff --git a/display-gtk.h b/display-gtk.h index a7279fa9a..74d86ef6d 100644 --- a/display-gtk.h +++ b/display-gtk.h @@ -40,6 +40,7 @@ struct preferences { visible_cols_t visible_cols; partial_pressure_graphs_t pp_graphs; gboolean profile_red_ceiling; + gboolean profile_calc_ceiling; }; extern struct preferences prefs; diff --git a/gtk-gui.c b/gtk-gui.c index 139775c08..9637ba532 100644 --- a/gtk-gui.c +++ b/gtk-gui.c @@ -511,6 +511,7 @@ OPTIONCALLBACK(po2_toggle, prefs.pp_graphs.po2) OPTIONCALLBACK(pn2_toggle, prefs.pp_graphs.pn2) OPTIONCALLBACK(phe_toggle, prefs.pp_graphs.phe) OPTIONCALLBACK(red_ceiling_toggle, prefs.profile_red_ceiling) +OPTIONCALLBACK(calc_ceiling_toggle, prefs.profile_calc_ceiling) OPTIONCALLBACK(force_toggle, force_download) OPTIONCALLBACK(prefer_dl_toggle, prefer_downloaded) @@ -768,11 +769,16 @@ static void preferences_dialog(GtkWidget *w, gpointer data) box = gtk_hbox_new(FALSE, 6); gtk_container_add(GTK_CONTAINER(vbox), box); - button = gtk_check_button_new_with_label(_("Show ceiling in red")); + button = gtk_check_button_new_with_label(_("Show dc reported ceiling in red")); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), prefs.profile_red_ceiling); gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6); g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(red_ceiling_toggle), NULL); + button = gtk_check_button_new_with_label(_("Show calculated ceiling")); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), prefs.profile_calc_ceiling); + gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6); + g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(calc_ceiling_toggle), NULL); + gtk_widget_show_all(dialog); result = gtk_dialog_run(GTK_DIALOG(dialog)); if (result == GTK_RESPONSE_ACCEPT) { @@ -817,6 +823,7 @@ static void preferences_dialog(GtkWidget *w, gpointer data) subsurface_set_conf("pn2threshold", PREF_STRING, pn2_threshold_text); subsurface_set_conf("phethreshold", PREF_STRING, phe_threshold_text); subsurface_set_conf("redceiling", PREF_BOOL, BOOL_TO_PTR(prefs.profile_red_ceiling)); + subsurface_set_conf("calcceiling", PREF_BOOL, BOOL_TO_PTR(prefs.profile_calc_ceiling)); new_default = strdup(gtk_button_get_label(GTK_BUTTON(xmlfile_button))); @@ -1236,6 +1243,7 @@ void init_ui(int *argcp, char ***argvp) free((void *)conf_value); } prefs.profile_red_ceiling = PTR_TO_BOOL(subsurface_get_conf("redceiling", PREF_BOOL)); + prefs.profile_calc_ceiling = PTR_TO_BOOL(subsurface_get_conf("calcceiling", PREF_BOOL)); divelist_font = subsurface_get_conf("divelist_font", PREF_STRING); default_filename = subsurface_get_conf("default_filename", PREF_STRING); diff --git a/profile.c b/profile.c index 88f2cb80e..6cc8ce074 100644 --- a/profile.c +++ b/profile.c @@ -78,7 +78,7 @@ typedef enum { TEXT_BACKGROUND, ALERT_BG, ALERT_FG, EVENTS, SAMPLE_DEEP, SAMPLE_SHALLOW, SMOOTHED, MINUTE, TIME_GRID, TIME_TEXT, DEPTH_GRID, MEAN_DEPTH, DEPTH_TOP, DEPTH_BOTTOM, TEMP_TEXT, TEMP_PLOT, SAC_DEFAULT, BOUNDING_BOX, PRESSURE_TEXT, BACKGROUND, - CEILING_SHALLOW, CEILING_DEEP + CEILING_SHALLOW, CEILING_DEEP, CALC_CEILING_SHALLOW, CALC_CEILING_DEEP } color_indice_t; typedef struct { @@ -136,6 +136,8 @@ static const color_t profile_color[] = { [BACKGROUND] = {{SPRINGWOOD1, BLACK1_LOW_TRANS}}, [CEILING_SHALLOW] = {{REDORANGE1_HIGH_TRANS, REDORANGE1_HIGH_TRANS}}, [CEILING_DEEP] = {{RED1_MED_TRANS, RED1_MED_TRANS}}, + [CALC_CEILING_SHALLOW] = {{FUNGREEN1_HIGH_TRANS, FUNGREEN1_HIGH_TRANS}}, + [CALC_CEILING_DEEP] = {{APPLE1_HIGH_TRANS, APPLE1_HIGH_TRANS}}, }; @@ -807,6 +809,24 @@ static void plot_depth_profile(struct graphics_context *gc, struct plot_info *pi cairo_close_path(gc->cr); cairo_fill(gc->cr); } + /* finally, plot the calculated ceiling over all this */ + if (prefs.profile_calc_ceiling) { + pat = cairo_pattern_create_linear (0.0, 0.0, 0.0, 256.0 * plot_scale); + pattern_add_color_stop_rgba (gc, pat, 0, CALC_CEILING_SHALLOW); + pattern_add_color_stop_rgba (gc, pat, 1, CALC_CEILING_DEEP); + cairo_set_source(gc->cr, pat); + cairo_pattern_destroy(pat); + entry = pi->entry; + move_to(gc, 0, 0); + for (i = 0; i < pi->nr; i++, entry++) { + if (entry->ceiling) + line_to(gc, entry->sec, entry->ceiling); + else + line_to(gc, entry->sec, 0); + } + cairo_close_path(gc->cr); + cairo_fill(gc->cr); + } /* next show where we have been bad and crossed the ceiling */ pat = cairo_pattern_create_linear (0.0, 0.0, 0.0, 256.0 * plot_scale); pattern_add_color_stop_rgba (gc, pat, 0, CEILING_SHALLOW); -- cgit v1.2.3-70-g09d2 From 5ba250bd48500c9f7aa206324272d40fd3064069 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Wed, 2 Jan 2013 23:22:07 -0800 Subject: Use gradient factors in deco calculation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Usually dive computers show the ceiling in terms of the next deco stop - and those are in 3m increments. This commit also adds the ability to chose either the typical 3m increments or the smooth ceiling that the Bühlmann algorithm actually calculates. Signed-off-by: Dirk Hohndel --- deco.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++++++----- display-gtk.h | 1 + dive.h | 2 +- gtk-gui.c | 8 ++++++ profile.c | 8 ++---- 5 files changed, 86 insertions(+), 13 deletions(-) diff --git a/deco.c b/deco.c index 6901e6c6a..998d6637c 100644 --- a/deco.c +++ b/deco.c @@ -8,6 +8,8 @@ * * clear_deco() - call to initialize for a new deco calculation * add_segment(pressure, gasmix) - add 1 second at the given pressure, breathing gasmix + * deco_allowed_depth(tissues_tolerance, surface_pressure, dive, smooth) + * - ceiling based on lead tissue, surface pressure, 3m increments or smooth */ #include "dive.h" @@ -24,9 +26,8 @@ struct buehlmann_config { double gf_high_emergency; //! emergency gf factors double gf_low_emergency; //! gradient factor low (at bottom/start of deco calculation). }; - -struct dive_data -{ +struct buehlmann_config buehlmann_config = { 1.0, 1.01, 0.5, 3, 75.0, 35.0, 10.0, 30.0, 95.0, 95.0 }; +struct dive_data { double pressure; //! pesent ambient pressure double surface; //! pressure at water surface struct gasmix *gasmix; //! current selected gas @@ -75,14 +76,50 @@ const double buehlmann_He_factor_expositon_one_second[] = { 1.00198406028040E-004, 7.83611475491108E-005, 6.13689891868496E-005, 4.81280465299827E-005}; #define WV_PRESSURE 0.0627 /* water vapor pressure */ +#define DIST_FROM_3_MTR 0.28 +#define PRESSURE_CHANGE_3M 0.3 +#define TOLERANCE 0.02 double tissue_n2_sat[16]; double tissue_he_sat[16]; double tissue_tolerated_ambient_pressure[16]; int ci_pointing_to_guiding_tissue; +double gf_low_position_this_dive; int divetime; -struct buehlmann_config buehlmann_config = { 1.0, 1.01, 0.5, 3, 95.0, 95.0, 10.0, 30.0, 95.0, 95.0 }; + + +static double actual_gradient_limit(const struct dive_data *data) +{ + double pressure_diff, limit_at_position; + double gf_high = buehlmann_config.gf_high; + double gf_low = buehlmann_config.gf_low; + + pressure_diff = data->pressure - data->surface; + + if (pressure_diff > TOLERANCE) { + if (pressure_diff < gf_low_position_this_dive) + limit_at_position = gf_high - ((gf_high - gf_low) * pressure_diff / gf_low_position_this_dive); + else + limit_at_position = gf_low; + } else { + limit_at_position = gf_high; + } + return limit_at_position; +} + +static double gradient_factor_calculation(const struct dive_data *data) +{ + double tissue_inertgas_saturation; + + tissue_inertgas_saturation = tissue_n2_sat[ci_pointing_to_guiding_tissue] + + tissue_he_sat[ci_pointing_to_guiding_tissue]; + if (tissue_inertgas_saturation < data->pressure) + return 0.0; + else + return (tissue_inertgas_saturation - data->pressure) / + (tissue_inertgas_saturation - tissue_tolerated_ambient_pressure[ci_pointing_to_guiding_tissue]); +} static double tissue_tolerance_calc(void) { @@ -104,7 +141,6 @@ static double tissue_tolerance_calc(void) ret_tolerance_limit_ambient_pressure = tissue_tolerated_ambient_pressure[ci]; } } - printf("%d:%02u %lf\n",FRACTION(divetime, 60), ret_tolerance_limit_ambient_pressure); return (ret_tolerance_limit_ambient_pressure); } @@ -116,7 +152,6 @@ double add_segment(double pressure, struct gasmix *gasmix) double pphe = (pressure - WV_PRESSURE) * gasmix->he.permille / 1000.0; divetime++; - printf("%2d:%02u N2 %2.3lf He %2.3lf",FRACTION(divetime, 60), ppn2, pphe); /* right now we just do OC */ for (ci = 0; ci < 16; ci++) { if (ppn2 - tissue_n2_sat[ci] > 0) @@ -141,3 +176,36 @@ void clear_deco() } divetime = 0; } + +unsigned int deco_allowed_depth(double tissues_tolerance, double surface_pressure, struct dive *dive, gboolean smooth) +{ + unsigned int depth, multiples_of_3m; + gboolean below_gradient_limit; + double new_gradient_factor; + double pressure_delta = tissues_tolerance - surface_pressure; + struct dive_data mydata; + + if (pressure_delta > 0) { + if (!smooth) { + multiples_of_3m = (pressure_delta + DIST_FROM_3_MTR) / 0.3; + depth = 3000 * multiples_of_3m; + } else { + depth = rel_mbar_to_depth(pressure_delta * 1000, dive); + } + } else { + depth = 0; + } + mydata.pressure = surface_pressure + depth / 10000.0; + mydata.surface = surface_pressure; + + new_gradient_factor = gradient_factor_calculation(&mydata); + below_gradient_limit = (new_gradient_factor < actual_gradient_limit(&mydata)); + while(!below_gradient_limit) + { + mydata.pressure += PRESSURE_CHANGE_3M; + new_gradient_factor = gradient_factor_calculation(&mydata); + below_gradient_limit = (new_gradient_factor < actual_gradient_limit(&mydata)); + } + + return depth; +} diff --git a/display-gtk.h b/display-gtk.h index 74d86ef6d..0523dc0aa 100644 --- a/display-gtk.h +++ b/display-gtk.h @@ -41,6 +41,7 @@ struct preferences { partial_pressure_graphs_t pp_graphs; gboolean profile_red_ceiling; gboolean profile_calc_ceiling; + gboolean calc_ceiling_3m_incr; }; extern struct preferences prefs; diff --git a/dive.h b/dive.h index b5d3368a5..e2af00d9d 100644 --- a/dive.h +++ b/dive.h @@ -574,7 +574,7 @@ extern void subsurface_command_line_exit(gint *, gchar ***); extern double add_segment(double pressure, struct gasmix *gasmix); extern void clear_deco(void); - +extern unsigned int deco_allowed_depth(double tissues_tolerance, double surface_pressure, struct dive *dive, gboolean smooth); #ifdef DEBUGFILE extern char *debugfilename; extern FILE *debugfile; diff --git a/gtk-gui.c b/gtk-gui.c index 9637ba532..79003ac2f 100644 --- a/gtk-gui.c +++ b/gtk-gui.c @@ -512,6 +512,7 @@ OPTIONCALLBACK(pn2_toggle, prefs.pp_graphs.pn2) OPTIONCALLBACK(phe_toggle, prefs.pp_graphs.phe) OPTIONCALLBACK(red_ceiling_toggle, prefs.profile_red_ceiling) OPTIONCALLBACK(calc_ceiling_toggle, prefs.profile_calc_ceiling) +OPTIONCALLBACK(calc_ceiling_3m_toggle, prefs.calc_ceiling_3m_incr) OPTIONCALLBACK(force_toggle, force_download) OPTIONCALLBACK(prefer_dl_toggle, prefer_downloaded) @@ -779,6 +780,11 @@ static void preferences_dialog(GtkWidget *w, gpointer data) gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6); g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(calc_ceiling_toggle), NULL); + button = gtk_check_button_new_with_label(_("3m increments for calculated ceiling")); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), prefs.calc_ceiling_3m_incr); + gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6); + g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(calc_ceiling_3m_toggle), NULL); + gtk_widget_show_all(dialog); result = gtk_dialog_run(GTK_DIALOG(dialog)); if (result == GTK_RESPONSE_ACCEPT) { @@ -824,6 +830,7 @@ static void preferences_dialog(GtkWidget *w, gpointer data) subsurface_set_conf("phethreshold", PREF_STRING, phe_threshold_text); subsurface_set_conf("redceiling", PREF_BOOL, BOOL_TO_PTR(prefs.profile_red_ceiling)); subsurface_set_conf("calcceiling", PREF_BOOL, BOOL_TO_PTR(prefs.profile_calc_ceiling)); + subsurface_set_conf("calcceiling3m", PREF_BOOL, BOOL_TO_PTR(prefs.calc_ceiling_3m_incr)); new_default = strdup(gtk_button_get_label(GTK_BUTTON(xmlfile_button))); @@ -1244,6 +1251,7 @@ void init_ui(int *argcp, char ***argvp) } prefs.profile_red_ceiling = PTR_TO_BOOL(subsurface_get_conf("redceiling", PREF_BOOL)); prefs.profile_calc_ceiling = PTR_TO_BOOL(subsurface_get_conf("calcceiling", PREF_BOOL)); + prefs.calc_ceiling_3m_incr = PTR_TO_BOOL(subsurface_get_conf("calcceiling3m", PREF_BOOL)); divelist_font = subsurface_get_conf("divelist_font", PREF_STRING); default_filename = subsurface_get_conf("default_filename", PREF_STRING); diff --git a/profile.c b/profile.c index 6cc8ce074..656e286bd 100644 --- a/profile.c +++ b/profile.c @@ -1560,7 +1560,7 @@ static struct plot_info *create_plot_info(struct dive *dive, struct divecomputer struct plot_data *entry = NULL; struct event *ev; double amb_pressure, po2; - int surface_pressure = dive->surface_pressure.mbar ? dive->surface_pressure.mbar : 1013; + double surface_pressure = (dive->surface_pressure.mbar ? dive->surface_pressure.mbar : 1013) / 1000.0; /* The plot-info is embedded in the graphics context */ pi = &gc->pi; @@ -1760,11 +1760,7 @@ static struct plot_info *create_plot_info(struct dive *dive, struct divecomputer if (min_pressure > ceiling_pressure) ceiling_pressure = min_pressure; } - ceiling_pressure = ceiling_pressure * 1000.0 + 0.5; - if (ceiling_pressure > surface_pressure) - entry->ceiling = rel_mbar_to_depth(ceiling_pressure - surface_pressure, dive); - else - entry->ceiling = 0; + entry->ceiling = deco_allowed_depth(ceiling_pressure, surface_pressure, dive, !prefs.calc_ceiling_3m_incr); } } -- cgit v1.2.3-70-g09d2 From 2c336032568cd13bec23fce2500bde551141c3e3 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Thu, 3 Jan 2013 20:45:20 -0800 Subject: Consider previous dives when calculating deco This also initializes the N2 tissue saturations to correct numbers (setting them to zero was clearly silly). With this commit we walk back in the dive_table until we find a surface intervall that's longer than 48h. Or a dive that comes after the last one we looked at; that would indicate that this is a divelist that contains dives from multiple divers or dives that for other reasons are not ordered. In a sane environment one would assume that the dives that need to be taken into account when doing deco calculations are organized as one trip in the XML file and so this logic should work. One major downside of the current implementation is that we recalculate everything whenever the plot_info is recreated - which happens quite frequently, for example when resizing the window or even when we go into loup mode. While this isn't all that compute intensive, this is an utter waste and we should at least cache the saturation inherited from previous dives (and clear that number when the selected dive changes). We don't want to cache all of it as the recreation of the plot_info may be triggered by the user changing equipment (and most importantly, gasmix) information. In that case the deco data for this dive does indeed have to be recreated. But without changing the current dive the saturation after the last surface intervall should stay the same. Signed-off-by: Dirk Hohndel --- deco.c | 63 ++++++++++++++++++++++++++++++++++++++------------ dive.h | 5 ++-- divelist.c | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ divelist.h | 1 + profile.c | 9 ++++++-- 5 files changed, 137 insertions(+), 18 deletions(-) diff --git a/deco.c b/deco.c index 998d6637c..e18779ac8 100644 --- a/deco.c +++ b/deco.c @@ -7,10 +7,11 @@ * The implementation below is (C) Dirk Hohndel 2012 and released under the GPLv2 * * clear_deco() - call to initialize for a new deco calculation - * add_segment(pressure, gasmix) - add 1 second at the given pressure, breathing gasmix + * add_segment(pressure, gasmix, seconds) - add at the given pressure, breathing gasmix * deco_allowed_depth(tissues_tolerance, surface_pressure, dive, smooth) * - ceiling based on lead tissue, surface pressure, 3m increments or smooth */ +#include #include "dive.h" //! Option structure for Buehlmann decompression. @@ -76,6 +77,7 @@ const double buehlmann_He_factor_expositon_one_second[] = { 1.00198406028040E-004, 7.83611475491108E-005, 6.13689891868496E-005, 4.81280465299827E-005}; #define WV_PRESSURE 0.0627 /* water vapor pressure */ +#define N2_IN_AIR 0.7902 #define DIST_FROM_3_MTR 0.28 #define PRESSURE_CHANGE_3M 0.3 #define TOLERANCE 0.02 @@ -145,32 +147,65 @@ static double tissue_tolerance_calc(void) } /* add a second at the given pressure and gas to the deco calculation */ -double add_segment(double pressure, struct gasmix *gasmix) +double add_segment(double pressure, struct gasmix *gasmix, int period_in_seconds) { int ci; double ppn2 = (pressure - WV_PRESSURE) * (1000 - gasmix->o2.permille - gasmix->he.permille) / 1000.0; double pphe = (pressure - WV_PRESSURE) * gasmix->he.permille / 1000.0; - divetime++; /* right now we just do OC */ - for (ci = 0; ci < 16; ci++) { - if (ppn2 - tissue_n2_sat[ci] > 0) - tissue_n2_sat[ci] += buehlmann_config.satmult * (ppn2 - tissue_n2_sat[ci]) * buehlmann_N2_factor_expositon_one_second[ci]; - else - tissue_n2_sat[ci] += buehlmann_config.desatmult * (ppn2 - tissue_n2_sat[ci]) * buehlmann_N2_factor_expositon_one_second[ci]; - if (pphe - tissue_he_sat[ci] > 0) - tissue_he_sat[ci] += buehlmann_config.satmult * (pphe - tissue_he_sat[ci]) * buehlmann_He_factor_expositon_one_second[ci]; - else - tissue_he_sat[ci] += buehlmann_config.desatmult * (pphe - tissue_he_sat[ci]) * buehlmann_He_factor_expositon_one_second[ci]; + if (period_in_seconds == 1) { /* that's what we do during the dive */ + for (ci = 0; ci < 16; ci++) { + if (ppn2 - tissue_n2_sat[ci] > 0) + tissue_n2_sat[ci] += buehlmann_config.satmult * (ppn2 - tissue_n2_sat[ci]) * + buehlmann_N2_factor_expositon_one_second[ci]; + else + tissue_n2_sat[ci] += buehlmann_config.desatmult * (ppn2 - tissue_n2_sat[ci]) * + buehlmann_N2_factor_expositon_one_second[ci]; + if (pphe - tissue_he_sat[ci] > 0) + tissue_he_sat[ci] += buehlmann_config.satmult * (pphe - tissue_he_sat[ci]) * + buehlmann_He_factor_expositon_one_second[ci]; + else + tissue_he_sat[ci] += buehlmann_config.desatmult * (pphe - tissue_he_sat[ci]) * + buehlmann_He_factor_expositon_one_second[ci]; + } + } else { /* all other durations */ + for (ci = 0; ci < 16; ci++) + { + if (ppn2 - tissue_n2_sat[ci] > 0) + tissue_n2_sat[ci] += buehlmann_config.satmult * (ppn2 - tissue_n2_sat[ci]) * + (1 - pow(2.0,(- period_in_seconds / (buehlmann_N2_t_halflife[ci] * 60)))); + else + tissue_n2_sat[ci] += buehlmann_config.desatmult * (ppn2 - tissue_n2_sat[ci]) * + (1 - pow(2.0,(- period_in_seconds / (buehlmann_N2_t_halflife[ci] * 60)))); + if (pphe - tissue_he_sat[ci] > 0) + tissue_he_sat[ci] += buehlmann_config.satmult * (pphe - tissue_he_sat[ci]) * + (1 - pow(2.0,(- period_in_seconds / (buehlmann_He_t_halflife[ci] * 60)))); + else + tissue_he_sat[ci] += buehlmann_config.desatmult * (pphe - tissue_he_sat[ci]) * + (1 - pow(2.0,(- period_in_seconds / (buehlmann_He_t_halflife[ci] * 60)))); + } } return tissue_tolerance_calc(); } -void clear_deco() +void dump_tissues() +{ + int ci; + printf("N2 tissues:"); + for (ci = 0; ci < 16; ci++) + printf(" %6.3e", tissue_n2_sat[ci]); + printf("\nHe tissues:"); + for (ci = 0; ci < 16; ci++) + printf(" %6.3e", tissue_he_sat[ci]); + printf("\n"); +} + +void clear_deco(double surface_pressure) { int ci; for (ci = 0; ci < 16; ci++) { - tissue_n2_sat[ci] = 0.0; + tissue_n2_sat[ci] = (surface_pressure - WV_PRESSURE) * N2_IN_AIR; tissue_he_sat[ci] = 0.0; tissue_tolerated_ambient_pressure[ci] = 0.0; } diff --git a/dive.h b/dive.h index e2af00d9d..0cee818fe 100644 --- a/dive.h +++ b/dive.h @@ -572,8 +572,9 @@ extern void subsurface_command_line_exit(gint *, gchar ***); #define FRACTION(n,x) ((unsigned)(n)/(x)),((unsigned)(n)%(x)) -extern double add_segment(double pressure, struct gasmix *gasmix); -extern void clear_deco(void); +extern double add_segment(double pressure, struct gasmix *gasmix, int period_in_seconds); +extern void clear_deco(double surface_pressure); +extern void dump_tissues(void); extern unsigned int deco_allowed_depth(double tissues_tolerance, double surface_pressure, struct dive *dive, gboolean smooth); #ifdef DEBUGFILE extern char *debugfilename; diff --git a/divelist.c b/divelist.c index 5df8fbb57..32f5f44a3 100644 --- a/divelist.c +++ b/divelist.c @@ -818,6 +818,83 @@ static int calculate_sac(struct dive *dive, struct divecomputer *dc) return sac * 1000; } +/* for now we do this based on the first divecomputer */ +static void add_dive_to_deco(struct dive *dive) +{ + struct divecomputer *dc = &dive->dc; + int i; + + if (!dc) + return; + for (i = 1; i < dive->dc.samples; i++) { + struct sample *psample = dc->sample + i - 1; + struct sample *sample = dc->sample + i; + int t0 = psample->time.seconds; + int t1 = sample->time.seconds; + int j; + + for (j = t0; j < t1; j++) { + int depth = 0.5 + psample->depth.mm + (j - t0) * (sample->depth.mm - psample->depth.mm) / (t1 - t0); + (void) add_segment(depth_to_mbar(depth, dive) / 1000.0, &dive->cylinder[sample->sensor].gasmix, 1); + } + } +} + +static struct gasmix air = { .o2.permille = 209 }; + +/* take into account previous dives until there is a 48h gap between dives */ +void init_decompression(struct dive *dive) +{ + int i, divenr = -1; + timestamp_t when; + gboolean deco_init = FALSE; + + if (!dive) + return; + while (++divenr < dive_table.nr && get_dive(divenr) != dive) + ; + when = dive->when; + i = divenr; + while (--i) { + struct dive* pdive = get_dive(i); + if (!pdive || pdive->when > when || pdive->when + pdive->duration.seconds + 48 * 60 * 60 < when) + break; + when = pdive->when; + } + + while (++i < divenr) { + struct dive* pdive = get_dive(i); + double surface_pressure = pdive->surface_pressure.mbar ? pdive->surface_pressure.mbar / 1000.0 : 1.013; + unsigned int surface_time = get_dive(i+1)->when - pdive->when - pdive->duration.seconds; + + if (!deco_init) { + clear_deco(surface_pressure); + deco_init = TRUE; +#if DEBUG & 16 + dump_tissues(); +#endif + } + add_dive_to_deco(pdive); +#if DEBUG & 16 + printf("added dive #%d\n", pdive->number); + dump_tissues(); +#endif + add_segment(surface_pressure, &air, surface_time); +#if DEBUG & 16 + printf("after surface intervall of %d:%02u\n", FRACTION(surface_time,60)); + dump_tissues(); +#endif + } + if (!deco_init) { + double surface_pressure = dive->surface_pressure.mbar ? dive->surface_pressure.mbar / 1000.0 : 1.013; + clear_deco(surface_pressure); +#if DEBUG & 16 + printf("no previous dive\n"); + dump_tissues(); +#endif + } +} + void update_cylinder_related_info(struct dive *dive) { if (dive != NULL) { diff --git a/divelist.h b/divelist.h index b309e9a54..41a9f38ae 100644 --- a/divelist.h +++ b/divelist.h @@ -15,4 +15,5 @@ extern void remember_tree_state(void); extern void restore_tree_state(void); extern void select_next_dive(void); extern void select_prev_dive(void); +extern void init_decompression(struct dive * dive); #endif diff --git a/profile.c b/profile.c index 656e286bd..32a91d2e6 100644 --- a/profile.c +++ b/profile.c @@ -1566,7 +1566,8 @@ static struct plot_info *create_plot_info(struct dive *dive, struct divecomputer pi = &gc->pi; /* reset deco information to start the calculation */ - clear_deco(); + init_decompression(dive); + /* we want to potentially add synthetic plot_info elements for the gas changes */ nr = dc->samples + 4 + 2 * count_gas_change_events(dc); if (last_pi_entry) @@ -1756,13 +1757,17 @@ static struct plot_info *create_plot_info(struct dive *dive, struct divecomputer float ceiling_pressure = 0; for (j = t0; j < t1; j++) { int depth = 0.5 + (entry - 1)->depth + (j - t0) * (entry->depth - (entry - 1)->depth) / (t1 - t0); - double min_pressure = add_segment(depth_to_mbar(depth, dive) / 1000.0, &dive->cylinder[cylinderindex].gasmix); + double min_pressure = add_segment(depth_to_mbar(depth, dive) / 1000.0, + &dive->cylinder[cylinderindex].gasmix, 1); if (min_pressure > ceiling_pressure) ceiling_pressure = min_pressure; } entry->ceiling = deco_allowed_depth(ceiling_pressure, surface_pressure, dive, !prefs.calc_ceiling_3m_incr); } } +#if DECO_CALC_DEBUG + dump_tissues(); +#endif if (entry) current->t_end = entry->sec; -- cgit v1.2.3-70-g09d2 From 9cd18c43aaaccaa8ba0fbdf8e5a23ad6c7bbeb73 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Thu, 3 Jan 2013 21:31:22 -0800 Subject: Make GF values configurable There are a couple of issues with this commit: GtkEntry should emit the 'changed' signal when it is modified (so that changes in the preferences get applied right away) - but that doesn't appear to be working consistently. Also, this doesn't appear to affect the deco of any dives that I try it with. So my guess is something is wrong with the underlying deco algorithm. That's diappointing. Signed-off-by: Dirk Hohndel --- deco.c | 9 ++++++++ display-gtk.h | 2 ++ dive.h | 1 + gtk-gui.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 4 files changed, 76 insertions(+), 3 deletions(-) diff --git a/deco.c b/deco.c index e18779ac8..efcdfd39b 100644 --- a/deco.c +++ b/deco.c @@ -10,6 +10,7 @@ * add_segment(pressure, gasmix, seconds) - add at the given pressure, breathing gasmix * deco_allowed_depth(tissues_tolerance, surface_pressure, dive, smooth) * - ceiling based on lead tissue, surface pressure, 3m increments or smooth + * set_gf(gflow, gfhigh) - set Buehlmann gradient factors */ #include #include "dive.h" @@ -244,3 +245,11 @@ unsigned int deco_allowed_depth(double tissues_tolerance, double surface_pressur return depth; } + +void set_gf(double gflow, double gfhigh) +{ + if (gflow != -1.0) + buehlmann_config.gf_low = gflow; + if (gfhigh != -1.0) + buehlmann_config.gf_high = gfhigh; +} diff --git a/display-gtk.h b/display-gtk.h index 0523dc0aa..629972434 100644 --- a/display-gtk.h +++ b/display-gtk.h @@ -42,6 +42,8 @@ struct preferences { gboolean profile_red_ceiling; gboolean profile_calc_ceiling; gboolean calc_ceiling_3m_incr; + double gflow; + double gfhigh; }; extern struct preferences prefs; diff --git a/dive.h b/dive.h index 0cee818fe..57ba280e0 100644 --- a/dive.h +++ b/dive.h @@ -576,6 +576,7 @@ extern double add_segment(double pressure, struct gasmix *gasmix, int period_in_ extern void clear_deco(double surface_pressure); extern void dump_tissues(void); extern unsigned int deco_allowed_depth(double tissues_tolerance, double surface_pressure, struct dive *dive, gboolean smooth); +extern void set_gf(double gflow, double gfhigh); #ifdef DEBUGFILE extern char *debugfilename; extern FILE *debugfile; diff --git a/gtk-gui.c b/gtk-gui.c index 79003ac2f..1b1113cad 100644 --- a/gtk-gui.c +++ b/gtk-gui.c @@ -36,7 +36,7 @@ struct preferences prefs = { SI_UNITS, { TRUE, FALSE, }, { FALSE, FALSE, FALSE, 1.6, 4.0, 13.0}, - FALSE + FALSE, FALSE, FALSE, 30.0, 90.0 }; struct dcnicknamelist { @@ -516,6 +516,24 @@ OPTIONCALLBACK(calc_ceiling_3m_toggle, prefs.calc_ceiling_3m_incr) OPTIONCALLBACK(force_toggle, force_download) OPTIONCALLBACK(prefer_dl_toggle, prefer_downloaded) +static void gflow_edit(GtkWidget *w, gpointer _data) +{ + double gflow; + const char *buf; + buf = gtk_entry_get_text(GTK_ENTRY(w)); + sscanf(buf, "%lf", &gflow); + set_gf(prefs.gflow, -1.0); +} + +static void gfhigh_edit(GtkWidget *w, gpointer _data) +{ + double gfhigh; + const char *buf; + buf = gtk_entry_get_text(GTK_ENTRY(w)); + sscanf(buf, "%lf", &gfhigh); + set_gf(-1.0, prefs.gfhigh); +} + static void event_toggle(GtkWidget *w, gpointer _data) { gboolean *plot_ev = _data; @@ -577,7 +595,7 @@ static void preferences_dialog(GtkWidget *w, gpointer data) { int result; GtkWidget *dialog, *notebook, *font, *frame, *box, *vbox, *button, *xmlfile_button; - GtkWidget *entry_po2, *entry_pn2, *entry_phe; + GtkWidget *entry_po2, *entry_pn2, *entry_phe, *entry_gflow, *entry_gfhigh; const char *current_default, *new_default; char threshold_text[10], utf8_buf[128]; struct preferences oldprefs = prefs; @@ -775,6 +793,9 @@ static void preferences_dialog(GtkWidget *w, gpointer data) gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6); g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(red_ceiling_toggle), NULL); + box = gtk_hbox_new(FALSE, 6); + gtk_container_add(GTK_CONTAINER(vbox), box); + button = gtk_check_button_new_with_label(_("Show calculated ceiling")); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), prefs.profile_calc_ceiling); gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6); @@ -785,10 +806,31 @@ static void preferences_dialog(GtkWidget *w, gpointer data) gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6); g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(calc_ceiling_3m_toggle), NULL); + box = gtk_hbox_new(FALSE, 6); + gtk_container_add(GTK_CONTAINER(vbox), box); + + frame = gtk_frame_new(_("GFlow")); + gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 6); + entry_gflow = gtk_entry_new(); + gtk_entry_set_max_length(GTK_ENTRY(entry_gflow), 4); + snprintf(threshold_text, sizeof(threshold_text), "%.0f", prefs.gflow); + gtk_entry_set_text(GTK_ENTRY(entry_gflow), threshold_text); + gtk_container_add(GTK_CONTAINER(frame), entry_gflow); + g_signal_connect(G_OBJECT(entry_gflow), "changed", G_CALLBACK(gflow_edit), NULL); + + frame = gtk_frame_new(_("GFhigh")); + gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 6); + entry_gfhigh = gtk_entry_new(); + gtk_entry_set_max_length(GTK_ENTRY(entry_gfhigh), 4); + snprintf(threshold_text, sizeof(threshold_text), "%.0f", prefs.gfhigh); + gtk_entry_set_text(GTK_ENTRY(entry_gfhigh), threshold_text); + gtk_container_add(GTK_CONTAINER(frame), entry_gfhigh); + g_signal_connect(G_OBJECT(entry_gflow), "changed", G_CALLBACK(gfhigh_edit), NULL); + gtk_widget_show_all(dialog); result = gtk_dialog_run(GTK_DIALOG(dialog)); if (result == GTK_RESPONSE_ACCEPT) { - const char *po2_threshold_text, *pn2_threshold_text, *phe_threshold_text; + const char *po2_threshold_text, *pn2_threshold_text, *phe_threshold_text, *gflow_text, *gfhigh_text; /* Make sure to flush any modified old dive data with old units */ update_dive(NULL); @@ -802,6 +844,11 @@ static void preferences_dialog(GtkWidget *w, gpointer data) sscanf(pn2_threshold_text, "%lf", &prefs.pp_graphs.pn2_threshold); phe_threshold_text = gtk_entry_get_text(GTK_ENTRY(entry_phe)); sscanf(phe_threshold_text, "%lf", &prefs.pp_graphs.phe_threshold); + gflow_text = gtk_entry_get_text(GTK_ENTRY(entry_gflow)); + sscanf(gflow_text, "%lf", &prefs.gflow); + gfhigh_text = gtk_entry_get_text(GTK_ENTRY(entry_gfhigh)); + sscanf(gfhigh_text, "%lf", &prefs.gfhigh); + set_gf(prefs.gflow, prefs.gfhigh); update_screen(); @@ -831,6 +878,8 @@ static void preferences_dialog(GtkWidget *w, gpointer data) subsurface_set_conf("redceiling", PREF_BOOL, BOOL_TO_PTR(prefs.profile_red_ceiling)); subsurface_set_conf("calcceiling", PREF_BOOL, BOOL_TO_PTR(prefs.profile_calc_ceiling)); subsurface_set_conf("calcceiling3m", PREF_BOOL, BOOL_TO_PTR(prefs.calc_ceiling_3m_incr)); + subsurface_set_conf("gflow", PREF_STRING, gflow_text); + subsurface_set_conf("gfhigh", PREF_STRING, gfhigh_text); new_default = strdup(gtk_button_get_label(GTK_BUTTON(xmlfile_button))); @@ -1252,6 +1301,18 @@ void init_ui(int *argcp, char ***argvp) prefs.profile_red_ceiling = PTR_TO_BOOL(subsurface_get_conf("redceiling", PREF_BOOL)); prefs.profile_calc_ceiling = PTR_TO_BOOL(subsurface_get_conf("calcceiling", PREF_BOOL)); prefs.calc_ceiling_3m_incr = PTR_TO_BOOL(subsurface_get_conf("calcceiling3m", PREF_BOOL)); + conf_value = subsurface_get_conf("gflow", PREF_STRING); + if (conf_value) { + sscanf(conf_value, "%lf", &prefs.gflow); + set_gf(prefs.gflow, -1.0); + free((void *)conf_value); + } + conf_value = subsurface_get_conf("gfhigh", PREF_STRING); + if (conf_value) { + sscanf(conf_value, "%lf", &prefs.gfhigh); + set_gf(-1.0, prefs.gfhigh); + free((void *)conf_value); + } divelist_font = subsurface_get_conf("divelist_font", PREF_STRING); default_filename = subsurface_get_conf("default_filename", PREF_STRING); -- cgit v1.2.3-70-g09d2