From 1b606ae2260395471890616f8b9dcb16e997fa3d Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Thu, 1 Nov 2012 10:04:12 -0700 Subject: First stab at plotting a pO2 graph So far this is done unconditionally. This already starts some of the infrastructure for other gases, but so far only O2 is handled. We also need a pressure scale on the right to make this useful - or we need to do peek / trough pressure prints like we do for temperature and depth. Finally, I think I want to move the plot further down, maybe make the whole plot area taller if we are plotting partial gas pressures as well. Signed-off-by: Dirk Hohndel --- color.h | 7 +++++-- display-gtk.h | 6 ++++++ profile.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 2 deletions(-) diff --git a/color.h b/color.h index 3299b7e8d..eb740fbd6 100644 --- a/color.h +++ b/color.h @@ -9,6 +9,7 @@ #define FUNGREEN1 { 0.0, 0.4, 0.2, 1 } #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 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 } @@ -38,12 +39,14 @@ // Blues #define GOVERNORBAY2 { 0.2, 0.2, 0.7, 1 } #define GOVERNORBAY1_MED_TRANS { 0.2, 0.2, 0.8, 0.5 } +#define ROYALBLUE2 { 0.2, 0.2, 0.9, 1 } #define ROYALBLUE2_LOW_TRANS { 0.2, 0.2, 0.9, 0.75 } -// Yellows +// Yellows / BROWNS #define SPRINGWOOD1 { 0.95, 0.95, 0.9, 1 } #define BROOM1_LOWER_TRANS { 1.0, 1.0, 0.1, 0.9 } - +#define PEANUT { 0.5, 0.2, 0.1, 1.0 } +#define PEANUT_MED_TRANS { 0.5, 0.2, 0.1, 0.5 } // Magentas #define MEDIUMREDVIOLET1_HIGHER_TRANS { 0.7, 0.2, 0.7, 0.1 } diff --git a/display-gtk.h b/display-gtk.h index f4961b301..879a02b4c 100644 --- a/display-gtk.h +++ b/display-gtk.h @@ -21,6 +21,12 @@ typedef struct { gboolean otu; } visible_cols_t; +typedef struct { + gboolean po2; + gboolean pn2; + gboolean phe; +} enabled_graphs_t; + typedef enum { PREF_BOOL, PREF_STRING diff --git a/profile.c b/profile.c index db2b1819b..7d3a4d5ee 100644 --- a/profile.c +++ b/profile.c @@ -43,6 +43,7 @@ struct plot_info { /* Depth info */ int depth; int smoothed; + double po2; velocity_t velocity; struct plot_data *min[3]; struct plot_data *max[3]; @@ -68,6 +69,9 @@ typedef enum { /* Velocity colors. Order is still important, ref VELOCITY_COLORS_START_IDX. */ VELO_STABLE, VELO_SLOW, VELO_MODERATE, VELO_FAST, VELO_CRAZY, + /* gas colors */ + PO2, PN2, PHE, + /* Other colors */ TEXT_BACKGROUND, ALERT_BG, ALERT_FG, EVENTS, SAMPLE_DEEP, SAMPLE_SHALLOW, SMOOTHED, MINUTE, TIME_GRID, TIME_TEXT, DEPTH_GRID, MEAN_DEPTH, DEPTH_TOP, @@ -99,6 +103,10 @@ static const color_t profile_color[] = { [VELO_FAST] = {{PIRATEGOLD1, BLACK1_LOW_TRANS}}, [VELO_CRAZY] = {{RED1, BLACK1_LOW_TRANS}}, + [PO2] = {{APPLE1, APPLE1_MED_TRANS}}, + [PN2] = {{ROYALBLUE2, ROYALBLUE2_LOW_TRANS}}, + [PHE] = {{PEANUT, PEANUT_MED_TRANS}}, + [TEXT_BACKGROUND] = {{CONCRETE1_LOWER_TRANS, WHITE1}}, [ALERT_BG] = {{BROOM1_LOWER_TRANS, BLACK1_LOW_TRANS}}, [ALERT_FG] = {{BLACK1_LOW_TRANS, BLACK1_LOW_TRANS}}, @@ -482,6 +490,27 @@ static void plot_depth_scale(struct graphics_context *gc, struct plot_info *pi) } } +static void plot_po2_profile(struct graphics_context *gc, struct plot_info *pi) +{ + int i; + struct plot_data *entry; + + gc->leftx = 0; + gc->rightx = get_maxtime(pi); + /* let's hope no one gets close to the top of that graph... */ + gc->topy = 3.0; gc->bottomy = 0.0; + + set_source_rgba(gc, PO2); + + entry = pi->entry; + move_to(gc, entry->sec, entry->po2); + for (i = 1; i < pi->nr; i++) { + entry++; + line_to(gc, entry->sec, entry->po2); + } + cairo_stroke(gc->cr); +} + static void plot_depth_profile(struct graphics_context *gc, struct plot_info *pi) { int i, incr; @@ -1257,6 +1286,7 @@ static struct plot_info *create_plot_info(struct dive *dive, int nr_samples, str lastdepth = -1; for (i = 0; i < nr_samples; i++) { int depth; + double fo2, pressure; int delay = 0; struct sample *sample = dive_sample+i; @@ -1297,6 +1327,10 @@ static struct plot_info *create_plot_info(struct dive *dive, int nr_samples, str depth = entry->depth = sample->depth.mm; entry->cylinderindex = sample->cylinderindex; SENSOR_PRESSURE(entry) = sample->cylinderpressure.mbar; + pressure = (depth + 10000) / 10000.0 * 1.01325; + fo2 = dive->cylinder[sample->cylinderindex].gasmix.o2.permille / 1000.0; + entry->po2 = fo2 * pressure; + entry->temperature = sample->temperature.mkelvin; if (depth || lastdepth) @@ -1367,10 +1401,17 @@ static struct plot_info *create_plot_info(struct dive *dive, int nr_samples, str pi->entry[i].same_cylinder = 1; pi->entry[i].cylinderindex = pi->entry[i-1].cylinderindex; INTERPOLATED_PRESSURE(pi->entry + i) = GET_PRESSURE(pi->entry + i - 1); + pi->entry[i].po2 = pi->entry[i-1].po2; pi->entry[i+1].sec = sec + 40; pi->entry[i+1].same_cylinder = 1; pi->entry[i+1].cylinderindex = pi->entry[i-1].cylinderindex; INTERPOLATED_PRESSURE(pi->entry + i + 1) = GET_PRESSURE(pi->entry + i - 1); + pi->entry[i+1].po2 = pi->entry[i-1].po2; + /* make sure the first two pi entries have a sane po2 */ + if (pi->entry[1].po2 < 0.01) + pi->entry[1].po2 = pi->entry[2].po2; + if (pi->entry[0].po2 < 0.01) + pi->entry[0].po2 = pi->entry[1].po2; /* the number of actual entries - some computers have lots of * depth 0 samples at the end of a dive, we want to make sure * we have exactly one of them at the end */ @@ -1486,6 +1527,9 @@ void plot(struct graphics_context *gc, cairo_rectangle_t *drawing_area, struct d cairo_close_path(gc->cr); cairo_stroke(gc->cr); +// if (graphs_enabled.po2) + plot_po2_profile(gc, pi); + /* now shift the translation back by half the margin; * this way we can draw the vertical scales on both sides */ cairo_translate(gc->cr, -drawing_area->x / 2.0, 0); -- cgit v1.2.3-70-g09d2 From bdc6b6ba243678e00c3e7f639358ee803ef5d85b Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Thu, 1 Nov 2012 11:11:05 -0700 Subject: Change preferences into a notebook and add second page for tec settings Not sure this is the best naming scheme (General Settings / Tec Settings) but it's a start. The idea is to have the settings that a recreational diver might care about on the first page, and all the other stuff on the second one. Let's see how this works out long term. For now I moved OTU over and added toggles for the different partial pressure graphs (only the pO2 one is implemented so far). Signed-off-by: Dirk Hohndel --- display-gtk.h | 1 + gtk-gui.c | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++--------- profile.c | 5 ++-- 3 files changed, 69 insertions(+), 13 deletions(-) diff --git a/display-gtk.h b/display-gtk.h index 879a02b4c..ea4786b67 100644 --- a/display-gtk.h +++ b/display-gtk.h @@ -60,6 +60,7 @@ extern void quit(GtkWidget *w, gpointer data); extern int is_default_dive_computer_device(const char *name); extern visible_cols_t visible_cols; +extern enabled_graphs_t enabled_graphs; extern const char *divelist_font; extern void set_divelist_font(const char *); diff --git a/gtk-gui.c b/gtk-gui.c index fb992f644..e5b35f468 100644 --- a/gtk-gui.c +++ b/gtk-gui.c @@ -36,7 +36,8 @@ struct units output_units; static GtkWidget *dive_profile; -visible_cols_t visible_cols = {TRUE, FALSE}; +visible_cols_t visible_cols = {TRUE, FALSE, }; +enabled_graphs_t enabled_graphs = { FALSE, }; static const char *default_dive_computer_vendor; static const char *default_dive_computer_product; @@ -503,6 +504,9 @@ OPTIONCALLBACK(totalweight_toggle, visible_cols.totalweight) OPTIONCALLBACK(suit_toggle, visible_cols.suit) OPTIONCALLBACK(cylinder_toggle, visible_cols.cylinder) OPTIONCALLBACK(autogroup_toggle, autogroup) +OPTIONCALLBACK(po2_toggle, enabled_graphs.po2) +OPTIONCALLBACK(pn2_toggle, enabled_graphs.pn2) +OPTIONCALLBACK(phe_toggle, enabled_graphs.phe) static void event_toggle(GtkWidget *w, gpointer _data) { @@ -564,7 +568,7 @@ static void pick_default_file(GtkWidget *w, GtkButton *button) static void preferences_dialog(GtkWidget *w, gpointer data) { int result; - GtkWidget *dialog, *font, *frame, *box, *vbox, *button; + GtkWidget *dialog, *notebook, *font, *frame, *box, *vbox, *button; const char *current_default, *new_default; menu_units = output_units; @@ -576,8 +580,15 @@ static void preferences_dialog(GtkWidget *w, gpointer data) GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL); + /* create the notebook for the preferences and attach it to dialog */ + notebook = gtk_notebook_new(); + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), notebook, FALSE, FALSE, 5); + + /* vbox that holds the first notebook page */ + vbox = gtk_vbox_new(FALSE, 6); + gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vbox, + gtk_label_new(_("General Settings"))); frame = gtk_frame_new(_("Units")); - vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); box = gtk_vbox_new(FALSE, 6); @@ -609,7 +620,7 @@ static void preferences_dialog(GtkWidget *w, gpointer data) NULL); frame = gtk_frame_new(_("Show Columns")); - gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5); + gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); box = gtk_hbox_new(FALSE, 6); gtk_container_add(GTK_CONTAINER(frame), box); @@ -634,11 +645,6 @@ 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(sac_toggle), NULL); - button = gtk_check_button_new_with_label(_("OTU")); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), visible_cols.otu); - gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6); - g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(otu_toggle), NULL); - button = gtk_check_button_new_with_label(_("Weight")); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), visible_cols.totalweight); gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6); @@ -650,13 +656,13 @@ static void preferences_dialog(GtkWidget *w, gpointer data) g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(suit_toggle), NULL); frame = gtk_frame_new(_("Divelist Font")); - gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5); + gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); font = gtk_font_button_new_with_font(divelist_font); gtk_container_add(GTK_CONTAINER(frame),font); frame = gtk_frame_new(_("Misc. Options")); - gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5); + gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); box = gtk_hbox_new(FALSE, 6); gtk_container_add(GTK_CONTAINER(frame), box); @@ -675,6 +681,43 @@ static void preferences_dialog(GtkWidget *w, gpointer data) g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(pick_default_file), button); gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6); + /* vbox that holds the second notebook page */ + vbox = gtk_vbox_new(FALSE, 6); + gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vbox, + gtk_label_new(_("Tec Settings"))); + + frame = gtk_frame_new(_("Show Columns")); + gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); + + box = gtk_hbox_new(FALSE, 6); + gtk_container_add(GTK_CONTAINER(frame), box); + + button = gtk_check_button_new_with_label(_("OTU")); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), visible_cols.otu); + gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6); + g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(otu_toggle), NULL); + + frame = gtk_frame_new(_("Show Partial Pressure Graphs in Profile")); + gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); + + box = gtk_hbox_new(FALSE, 6); + gtk_container_add(GTK_CONTAINER(frame), box); + + button = gtk_check_button_new_with_label(_("pO" UTF8_SUBSCRIPT_2)); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), enabled_graphs.po2); + gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6); + g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(po2_toggle), NULL); + + button = gtk_check_button_new_with_label(_("pN" UTF8_SUBSCRIPT_2)); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), enabled_graphs.pn2); + gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6); + g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(pn2_toggle), NULL); + + button = gtk_check_button_new_with_label(_("pHe")); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), enabled_graphs.phe); + gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6); + g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(phe_toggle), NULL); + gtk_widget_show_all(dialog); result = gtk_dialog_run(GTK_DIALOG(dialog)); if (result == GTK_RESPONSE_ACCEPT) { @@ -696,6 +739,7 @@ static void preferences_dialog(GtkWidget *w, gpointer data) subsurface_set_conf("cuft", PREF_BOOL, BOOL_TO_PTR(output_units.volume == CUFT)); subsurface_set_conf("fahrenheit", PREF_BOOL, BOOL_TO_PTR(output_units.temperature == FAHRENHEIT)); subsurface_set_conf("lbs", PREF_BOOL, BOOL_TO_PTR(output_units.weight == LBS)); + subsurface_set_conf("TEMPERATURE", PREF_BOOL, BOOL_TO_PTR(visible_cols.temperature)); subsurface_set_conf("TOTALWEIGHT", PREF_BOOL, BOOL_TO_PTR(visible_cols.totalweight)); subsurface_set_conf("SUIT", PREF_BOOL, BOOL_TO_PTR(visible_cols.suit)); @@ -703,8 +747,14 @@ static void preferences_dialog(GtkWidget *w, gpointer data) subsurface_set_conf("NITROX", PREF_BOOL, BOOL_TO_PTR(visible_cols.nitrox)); subsurface_set_conf("SAC", PREF_BOOL, BOOL_TO_PTR(visible_cols.sac)); subsurface_set_conf("OTU", PREF_BOOL, BOOL_TO_PTR(visible_cols.otu)); + subsurface_set_conf("divelist_font", PREF_STRING, divelist_font); subsurface_set_conf("autogroup", PREF_BOOL, BOOL_TO_PTR(autogroup)); + + subsurface_set_conf("po2graph", PREF_BOOL, BOOL_TO_PTR(enabled_graphs.po2)); + subsurface_set_conf("pn2graph", PREF_BOOL, BOOL_TO_PTR(enabled_graphs.pn2)); + subsurface_set_conf("phegraph", PREF_BOOL, BOOL_TO_PTR(enabled_graphs.phe)); + new_default = strdup(gtk_button_get_label(GTK_BUTTON(button))); /* if we opened the default file and are changing its name, @@ -1052,6 +1102,10 @@ void init_ui(int *argcp, char ***argvp) visible_cols.otu = PTR_TO_BOOL(subsurface_get_conf("OTU", PREF_BOOL)); visible_cols.sac = PTR_TO_BOOL(subsurface_get_conf("SAC", PREF_BOOL)); + enabled_graphs.po2 = PTR_TO_BOOL(subsurface_get_conf("po2graph", PREF_BOOL)); + enabled_graphs.pn2 = PTR_TO_BOOL(subsurface_get_conf("pn2graph", PREF_BOOL)); + enabled_graphs.phe = PTR_TO_BOOL(subsurface_get_conf("phegraph", PREF_BOOL)); + divelist_font = subsurface_get_conf("divelist_font", PREF_STRING); autogroup = PTR_TO_BOOL(subsurface_get_conf("autogroup", PREF_BOOL)); default_filename = subsurface_get_conf("default_filename", PREF_STRING); diff --git a/profile.c b/profile.c index 7d3a4d5ee..1e6d523f3 100644 --- a/profile.c +++ b/profile.c @@ -11,6 +11,7 @@ #include "dive.h" #include "display.h" +#include "display-gtk.h" #include "divelist.h" #include "color.h" @@ -43,7 +44,7 @@ struct plot_info { /* Depth info */ int depth; int smoothed; - double po2; + double po2, pn2, phe; velocity_t velocity; struct plot_data *min[3]; struct plot_data *max[3]; @@ -1527,7 +1528,7 @@ void plot(struct graphics_context *gc, cairo_rectangle_t *drawing_area, struct d cairo_close_path(gc->cr); cairo_stroke(gc->cr); -// if (graphs_enabled.po2) + if (enabled_graphs.po2) plot_po2_profile(gc, pi); /* now shift the translation back by half the margin; -- cgit v1.2.3-70-g09d2 From 01fd6a57bce4cd501b23481591462161f2ae34d5 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Thu, 1 Nov 2012 11:44:18 -0700 Subject: Add vertical space to depth plot if we are showing partial pressure graphs Fairly simplistic change that modifies the way we calculate the "maxdepth" for a particular dive as that is used to scale the plot vertically. Signed-off-by: Dirk Hohndel --- display-gtk.h | 8 +++++--- profile.c | 16 ++++++++++++++-- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/display-gtk.h b/display-gtk.h index ea4786b67..22ff9d626 100644 --- a/display-gtk.h +++ b/display-gtk.h @@ -27,6 +27,11 @@ typedef struct { gboolean phe; } enabled_graphs_t; +extern visible_cols_t visible_cols; +extern enabled_graphs_t enabled_graphs; + +#define GRAPHS_ENABLED (enabled_graphs.po2 || enabled_graphs.pn2 || enabled_graphs.phe) + typedef enum { PREF_BOOL, PREF_STRING @@ -59,9 +64,6 @@ extern void quit(GtkWidget *w, gpointer data); extern int is_default_dive_computer_device(const char *name); -extern visible_cols_t visible_cols; -extern enabled_graphs_t enabled_graphs; - extern const char *divelist_font; extern void set_divelist_font(const char *); diff --git a/profile.c b/profile.c index 1e6d523f3..d32224190 100644 --- a/profile.c +++ b/profile.c @@ -220,16 +220,28 @@ static int get_maxtime(struct plot_info *pi) } } +/* get the maximum depth to which we want to plot + * take into account the additional verical space needed to plot + * partial pressure graphs */ static int get_maxdepth(struct plot_info *pi) { unsigned mm = pi->maxdepth; + int md; + if (zoomed_plot) { /* Rounded up to 10m, with at least 3m to spare */ - return ROUND_UP(mm+3000, 10000); + md = ROUND_UP(mm+3000, 10000); } else { /* Minimum 30m, rounded up to 10m, with at least 3m to spare */ - return MAX(30000, ROUND_UP(mm+3000, 10000)); + md = MAX(30000, ROUND_UP(mm+3000, 10000)); + } + if (GRAPHS_ENABLED) { + if (md <= 20000) + md += 10000; + else + md += ROUND_UP(md / 3, 10000); } + return md; } typedef struct { -- cgit v1.2.3-70-g09d2 From f806cdbe2eee8abbc0c9de75664ceef0bbb0d93f Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Thu, 1 Nov 2012 12:05:31 -0700 Subject: Fix the pO2 calculation when diving with air So few of my dives are on air that at first I didn't notice - but for those dives we set the o2 permille to 0 - which of course causes incorrect (and extremely deadly) pO2 of 0... Signed-off-by: Dirk Hohndel --- profile.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/profile.c b/profile.c index d32224190..82b7dd0f1 100644 --- a/profile.c +++ b/profile.c @@ -1298,8 +1298,8 @@ static struct plot_info *create_plot_info(struct dive *dive, int nr_samples, str lastindex = 0; lastdepth = -1; for (i = 0; i < nr_samples; i++) { - int depth; - double fo2, pressure; + int depth, fo2; + double pressure; int delay = 0; struct sample *sample = dive_sample+i; @@ -1341,8 +1341,10 @@ static struct plot_info *create_plot_info(struct dive *dive, int nr_samples, str entry->cylinderindex = sample->cylinderindex; SENSOR_PRESSURE(entry) = sample->cylinderpressure.mbar; pressure = (depth + 10000) / 10000.0 * 1.01325; - fo2 = dive->cylinder[sample->cylinderindex].gasmix.o2.permille / 1000.0; - entry->po2 = fo2 * pressure; + fo2 = dive->cylinder[sample->cylinderindex].gasmix.o2.permille; + if (!fo2) + fo2 = AIR_PERMILLE; + entry->po2 = fo2 / 1000.0 * pressure; entry->temperature = sample->temperature.mkelvin; -- cgit v1.2.3-70-g09d2 From 5f2f415cdbc7854fd6554a75bcf37d9462747a00 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Thu, 1 Nov 2012 15:39:15 -0700 Subject: Add pHe and pN2 plotting Adjust the color for pN2 to the standard for this gas (black). We keep pO2 green (even though the ISO 32 color for that would be white). pHe is marked in brown (which is the matching standard color). Calculate correct partial pressures for the synthetic plot info points at the beginning and end of the dive. Minor fine tuning to the positioning / scaling of the temperature plot when partial pressures are plotted. Signed-off-by: Dirk Hohndel --- profile.c | 99 +++++++++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 74 insertions(+), 25 deletions(-) diff --git a/profile.c b/profile.c index 82b7dd0f1..c0f49a113 100644 --- a/profile.c +++ b/profile.c @@ -105,7 +105,7 @@ static const color_t profile_color[] = { [VELO_CRAZY] = {{RED1, BLACK1_LOW_TRANS}}, [PO2] = {{APPLE1, APPLE1_MED_TRANS}}, - [PN2] = {{ROYALBLUE2, ROYALBLUE2_LOW_TRANS}}, + [PN2] = {{BLACK1_LOW_TRANS, BLACK1_LOW_TRANS}}, [PHE] = {{PEANUT, PEANUT_MED_TRANS}}, [TEXT_BACKGROUND] = {{CONCRETE1_LOWER_TRANS, WHITE1}}, @@ -184,11 +184,13 @@ static void dump_pi (struct plot_info *pi) pi->maxpressure, pi->mintemp, pi->maxtemp); for (i = 0; i < pi->nr; i++) printf(" entry[%d]:{same_cylinder:%d cylinderindex:%d sec:%d pressure:{%d,%d}\n" - " time:%d:%02d temperature:%d depth:%d smoothed:%d}\n", + " time:%d:%02d temperature:%d depth:%d smoothed:%d po2:%lf phe:%lf pn2:%lf sum-pp %lf}\n", i, pi->entry[i].same_cylinder, pi->entry[i].cylinderindex, pi->entry[i].sec, pi->entry[i].pressure[0], pi->entry[i].pressure[1], pi->entry[i].sec / 60, pi->entry[i].sec % 60, - pi->entry[i].temperature, pi->entry[i].depth, pi->entry[i].smoothed); + pi->entry[i].temperature, pi->entry[i].depth, pi->entry[i].smoothed, + pi->entry[i].po2, pi->entry[i].phe, pi->entry[i].pn2, + pi->entry[i].po2 + pi->entry[i].phe + pi->entry[i].pn2); printf(" }\n"); } @@ -503,25 +505,55 @@ static void plot_depth_scale(struct graphics_context *gc, struct plot_info *pi) } } -static void plot_po2_profile(struct graphics_context *gc, struct plot_info *pi) +static void plot_pp_gas_profile(struct graphics_context *gc, struct plot_info *pi) { int i; + int maxdepth; struct plot_data *entry; gc->leftx = 0; gc->rightx = get_maxtime(pi); - /* let's hope no one gets close to the top of that graph... */ - gc->topy = 3.0; gc->bottomy = 0.0; - set_source_rgba(gc, PO2); + /* the maxdepth already includes extra vertical space - and if + * we take the corresponding pressure as maximum partial + * pressure the graph seems to look fine*/ + maxdepth = get_maxdepth(pi); + gc->topy = (maxdepth + 10000) / 10000.0 * 1.01325; + gc->bottomy = 0.0; - entry = pi->entry; - move_to(gc, entry->sec, entry->po2); - for (i = 1; i < pi->nr; i++) { - entry++; - line_to(gc, entry->sec, entry->po2); + if (enabled_graphs.po2) { + set_source_rgba(gc, PO2); + + entry = pi->entry; + move_to(gc, entry->sec, entry->po2); + for (i = 1; i < pi->nr; i++) { + entry++; + line_to(gc, entry->sec, entry->po2); + } + cairo_stroke(gc->cr); + } + if (enabled_graphs.pn2) { + set_source_rgba(gc, PN2); + + entry = pi->entry; + move_to(gc, entry->sec, entry->pn2); + for (i = 1; i < pi->nr; i++) { + entry++; + line_to(gc, entry->sec, entry->pn2); + } + cairo_stroke(gc->cr); + } + if (enabled_graphs.phe) { + set_source_rgba(gc, PHE); + + entry = pi->entry; + move_to(gc, entry->sec, entry->phe); + for (i = 1; i < pi->nr; i++) { + entry++; + line_to(gc, entry->sec, entry->phe); + } + cairo_stroke(gc->cr); } - cairo_stroke(gc->cr); } static void plot_depth_profile(struct graphics_context *gc, struct plot_info *pi) @@ -656,13 +688,15 @@ static int setup_temperature_limits(struct graphics_context *gc, struct plot_inf /* Show temperatures in roughly the lower third, but make sure the scale is at least somewhat reasonable */ delta = maxtemp - mintemp; - if (delta > 3000) { /* more than 3K in fluctuation */ + if (delta > 3000) /* more than 3K in fluctuation */ gc->topy = maxtemp + delta*2; - gc->bottomy = mintemp - delta/2; - } else { + else gc->topy = maxtemp + 1500 + delta*2; + + if (GRAPHS_ENABLED) + gc->bottomy = mintemp - delta * 1.5; + else gc->bottomy = mintemp - delta/2; - } return maxtemp > mintemp; } @@ -1298,7 +1332,7 @@ static struct plot_info *create_plot_info(struct dive *dive, int nr_samples, str lastindex = 0; lastdepth = -1; for (i = 0; i < nr_samples; i++) { - int depth, fo2; + int depth, fo2, fhe; double pressure; int delay = 0; struct sample *sample = dive_sample+i; @@ -1342,9 +1376,13 @@ static struct plot_info *create_plot_info(struct dive *dive, int nr_samples, str SENSOR_PRESSURE(entry) = sample->cylinderpressure.mbar; pressure = (depth + 10000) / 10000.0 * 1.01325; fo2 = dive->cylinder[sample->cylinderindex].gasmix.o2.permille; + fhe = dive->cylinder[sample->cylinderindex].gasmix.he.permille; + if (!fo2) fo2 = AIR_PERMILLE; entry->po2 = fo2 / 1000.0 * pressure; + entry->phe = fhe / 1000.0 * pressure; + entry->pn2 = (1000 - fo2 - fhe) / 1000.0 * pressure; entry->temperature = sample->temperature.mkelvin; @@ -1416,17 +1454,28 @@ static struct plot_info *create_plot_info(struct dive *dive, int nr_samples, str pi->entry[i].same_cylinder = 1; pi->entry[i].cylinderindex = pi->entry[i-1].cylinderindex; INTERPOLATED_PRESSURE(pi->entry + i) = GET_PRESSURE(pi->entry + i - 1); - pi->entry[i].po2 = pi->entry[i-1].po2; + pi->entry[i].po2 = pi->entry[i-1].po2 / (pi->entry[i].depth + 10000.0) * 10000.0; + pi->entry[i].phe = pi->entry[i-1].phe / (pi->entry[i].depth + 10000.0) * 10000.0; + pi->entry[i].pn2 = 1.01325 - pi->entry[i].po2 - pi->entry[i].phe; pi->entry[i+1].sec = sec + 40; pi->entry[i+1].same_cylinder = 1; pi->entry[i+1].cylinderindex = pi->entry[i-1].cylinderindex; INTERPOLATED_PRESSURE(pi->entry + i + 1) = GET_PRESSURE(pi->entry + i - 1); - pi->entry[i+1].po2 = pi->entry[i-1].po2; - /* make sure the first two pi entries have a sane po2 */ + pi->entry[i+1].po2 = pi->entry[i].po2; + pi->entry[i+1].phe = pi->entry[i].phe; + pi->entry[i+1].pn2 = pi->entry[i].pn2; + /* make sure the first two pi entries have a sane po2 / phe / pn2 */ if (pi->entry[1].po2 < 0.01) - pi->entry[1].po2 = pi->entry[2].po2; + pi->entry[1].po2 = pi->entry[2].po2 / (pi->entry[2].depth + 10000.0) * 10000.0; + if (pi->entry[1].phe < 0.01) + pi->entry[1].phe = pi->entry[2].phe / (pi->entry[2].depth + 10000.0) * 10000.0; + pi->entry[1].pn2 = 1.01325 - pi->entry[1].po2 - pi->entry[1].phe; if (pi->entry[0].po2 < 0.01) - pi->entry[0].po2 = pi->entry[1].po2; + pi->entry[0].po2 = pi->entry[1].po2 / (pi->entry[1].depth + 10000.0) * 10000.0; + if (pi->entry[0].phe < 0.01) + pi->entry[0].phe = pi->entry[1].phe / (pi->entry[1].depth + 10000.0) * 10000.0; + pi->entry[0].pn2 = 1.01325 - pi->entry[0].po2 - pi->entry[0].phe; + /* the number of actual entries - some computers have lots of * depth 0 samples at the end of a dive, we want to make sure * we have exactly one of them at the end */ @@ -1542,8 +1591,8 @@ void plot(struct graphics_context *gc, cairo_rectangle_t *drawing_area, struct d cairo_close_path(gc->cr); cairo_stroke(gc->cr); - if (enabled_graphs.po2) - plot_po2_profile(gc, pi); + if (GRAPHS_ENABLED) + plot_pp_gas_profile(gc, pi); /* now shift the translation back by half the margin; * this way we can draw the vertical scales on both sides */ -- cgit v1.2.3-70-g09d2 From 853277ba9d821922a8da6e55dd47d2f09def50fa Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Mon, 5 Nov 2012 08:56:18 -0800 Subject: Plot text values for partial pressure graphs The algorithms attempt to identify "interesting" points where the user might want to know the value of the graph. Signed-off-by: Dirk Hohndel --- profile.c | 263 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 262 insertions(+), 1 deletion(-) diff --git a/profile.c b/profile.c index c0f49a113..60adbc0a7 100644 --- a/profile.c +++ b/profile.c @@ -8,6 +8,7 @@ #include #include #include +#include #include "dive.h" #include "display.h" @@ -505,6 +506,264 @@ static void plot_depth_scale(struct graphics_context *gc, struct plot_info *pi) } } +/* ap points to an array of int with pi->nr + 1 elements that is + * ininitialized with just one -1 entry + * this adds entries (if they aren't too close to an existing one) + * and keeps things sorted + * we KNOW the array is big enough to hold all possible indices + * a2p is a secondary array - we insert value at the same relative + * positio as idx in ap */ +static void add_index(int idx, int margin, int **ap, int **a2p, int value) +{ + int j, i = 0; + int *a = *ap; + int *a2 = *a2p; + + while (a[i] != -1 && a[i] < idx) + i++; + if (a[i] == idx) + return; + if (a[i] != -1 && a[i - 1] != -1 && idx - a[i - 1] < margin) + return; + if (a[i] != -1 && a[i] - idx < margin) + return; + j = i; + while (a[j] != -1) + j++; + while (j >= i) { + a[j+1] = a[j]; + a2[j+1] = a2[j]; + j--; + } + a[i] = idx; + a2[i] = value; +} + +#define LI(_i,_j) MAX((_i)-(_j), 0) +#define RI(_i,_j) MIN((_i)+(_j), nr - 1) +#define SPIKE(_i,_s) if (fabs(_s) > fabs(spk_data[_i])) spk_data[_i] = (_s) +/* this is an attempt at a metric that finds spikes in a data series */ +static void calculate_spikyness(int nr, double *data, double *spk_data, int deltax, double deltay) +{ + int i, j; + double dminl, dminr, dmaxl, dmaxr; + +#if DEBUG_PROFILE > 2 + printf("Spike data: \n 0 "); +#endif + for (i = 0; i < nr; i++) { + dminl = dminr = dmaxl = dmaxr = data[i]; + spk_data[i] = 0.0; + for (j = 1; j < deltax; j++) { + if (data[LI(i,j)] < dminl) + dminl = data[LI(i,j)]; + if (data[LI(i,j)] > dmaxl) + dmaxl = data[LI(i,j)]; + + if (data[RI(i,j)] < dminr) + dminr = data[RI(i,j)]; + if (data[RI(i,j)] > dmaxr) + dmaxr = data[RI(i,j)]; + + /* don't do super narrow */ + if (j < deltax / 3) + continue; + /* falling edge on left */ + if (dmaxl == data[i] && dmaxr - data[i] < 0.1 * (data[i] - dminl)) + SPIKE(i,(data[i] - dminl) / j); + /* falling edge on right */ + if (dmaxr == data[i] && dmaxl - data[i] < 0.1 * (data[i] - dminr)) + SPIKE(i,(data[i] - dminr) / j); + + /* minima get a negative spike value */ + /* rising edge on left */ + if (dminl == data[i] && data[i] - dminr < 0.1 * (dmaxl - data[i])) + SPIKE(i,(data[i] - dmaxl) / j); + /* rising edge on right */ + if (dminr == data[i] && data[i] - dminl < 0.1 * (dmaxr - data[i])) + SPIKE(i,(data[i] - dmaxr) / j); + } +#if DEBUG_PROFILE > 2 + fprintf(debugfile, "%.4lf ", spk_data[i]); + if (i % 12 == 11) + fprintf(debugfile, "\n%2d ", (i + 1) / 12); +#endif + } +#if DEBUG_PROFILE > 2 + printf("\n"); +#endif +} + +/* only show one spike in a deltax wide region - pick the highest (and first if the same) */ +static gboolean higher_spike(double *spk_data, int idx, int nr, int deltax) +{ + int i; + double s = fabs(spk_data[idx]); + for (i = MAX(0, idx - deltax); i <= MIN(idx + deltax, nr - 1); i++) + if (fabs(spk_data[i]) > s) + return TRUE; + else if (fabs(spk_data[i]) == s && i < idx) + return TRUE; + return FALSE; +} + +/* this figures out which time stamps provide "interesting" formations in the graphs; + * this includes local minima and maxima as well as long plateaus. + * pass in the function that returns the value at a certain point (as double), + * the delta in time (expressed as number of data points of "significant time") + * the delta at which the value is considered to have been "significantly changed" and + * the number of points to cover + * returns a list of indices that ends with a -1 of times that are "interesting" */ +static void find_points_of_interest(struct plot_info *pi, double (*value_func)(int, struct plot_info *), + int deltax, double deltay, int **poip, int **poip_vpos) +{ + int i, j, nr = pi->nr; + double *data, *data_max, *data_min, *spk_data; + double min, max; + int *pois; + + /* avoid all the function calls by creating a local array and + * have some helper arrays to make our lifes easier */ + + data = malloc(nr * sizeof(double)); + data_max = malloc(nr * sizeof(double)); + data_min = malloc(nr * sizeof(double)); + spk_data = malloc(nr * sizeof(double)); + + pois = *poip = malloc((nr + 1) * sizeof(int)); + *poip_vpos = malloc((nr + 1) * sizeof(int)); + pois[0] = -1; + pois[1] = -1; + + /* copy the data and get the absolute minimum and maximum while we do it */ + for (i = 0; i < nr; i++) { + data_max[i] = data_min[i] = data[i] = value_func(i, pi); + if (i == 0 || data[i] < min) + min = data[i]; + if (i == 0 || data[i] > max) + max = data[i]; + } + /* next find out if there are real spikes in the graph */ + calculate_spikyness(nr, data, spk_data, deltax, deltay); + + /* now process all data points */ + for (i = 0; i < nr; i++) { + /* get the local min/max */ + for (j = MAX(0, i - deltax); j < i + deltax && j < nr; j++) { + if (data[j] < data[i]) + data_min[i] = data[j]; + if (data[j] > data[i]) + data_max[i] = data[j]; + } + /* is i the overall minimum or maximum */ + if (data[i] == max) + add_index(i, deltax, poip, poip_vpos, BOTTOM); + if (data[i] == min) + add_index(i, deltax, poip, poip_vpos, TOP); + /* is i a spike? */ + if (fabs(spk_data[i]) > 0.01 && ! higher_spike(spk_data, i, nr, deltax)) { + if (spk_data[i] > 0.0) + add_index(i, deltax, poip, poip_vpos, BOTTOM); + if (spk_data[i] < 0.0) + add_index(i, deltax, poip, poip_vpos, TOP); + } + /* is i a significant local minimum or maximum? */ + if (data[i] == data_min[i] && data_max[i] - data[i] > deltay) + add_index(i, deltax, poip, poip_vpos, TOP); + if (data[i] == data_max[i] && data[i] - data_min[i] > deltay) + add_index(i, deltax, poip, poip_vpos, BOTTOM); + } + /* still need to search for plateaus */ +} + +static void setup_pp_limits(struct graphics_context *gc, struct plot_info *pi) +{ + int maxdepth; + + gc->leftx = 0; + gc->rightx = get_maxtime(pi); + + /* the maxdepth already includes extra vertical space - and if + * we take the corresponding pressure as maximum partial + * pressure the graph seems to look fine*/ + maxdepth = get_maxdepth(pi); + gc->topy = (maxdepth + 10000) / 10000.0 * 1.01325; + gc->bottomy = 0.0; +} + +static void plot_single_pp_text(struct graphics_context *gc, int sec, double pp, color_indice_t color) +{ + text_render_options_t tro = {12, color, CENTER, BOTTOM}; + plot_text(gc, &tro, sec, pp, "%.1lf", pp); + + if (color == PN2) + printf("pN2 %lf\n", pp); +} + +#define MAXPP(_mpp, _pp) { _mpp = 0; \ + for(i = 0; i< pi->nr; i++) \ + if (pi->entry[i]._pp > _mpp) \ + _mpp = pi->entry[i]._pp; \ + } + +static double po2_value(int idx, struct plot_info *pi) +{ + return pi->entry[idx].po2; +} + +static double pn2_value(int idx, struct plot_info *pi) +{ + return pi->entry[idx].pn2; +} + +static double phe_value(int idx, struct plot_info *pi) +{ + return pi->entry[idx].phe; +} + +static void plot_single_gas_pp_text(struct graphics_context *gc, struct plot_info *pi, + double (*value_func)(int, struct plot_info *), + double value_threshold, int color) +{ + int *pois, *pois_vpos; + int i, two_minutes = 1; + /* don't bother with local min/max if the dive is under two minutes */ + if (pi->entry[pi->nr - 1].sec > 120) { + int idx = 0; + while (pi->entry[idx].sec == 0) + idx++; + while (pi->entry[idx + two_minutes].sec < 120) + two_minutes++; + } else { + two_minutes = pi->nr; + } + find_points_of_interest(pi, value_func, two_minutes, value_threshold, &pois, &pois_vpos); + for (i = 0; pois[i] != -1; i++) { + struct plot_data *entry = pi->entry + pois[i]; +#if DEBUG_PROFILE > 1 + fprintf(debugfile, "POI at %d sec value %lf\n", entry->sec, entry->po2); +#endif + plot_single_pp_text(gc, entry->sec, value_func(pois[i], pi), color); + } + free(pois); + free(pois_vpos); +} + +static void plot_pp_text(struct graphics_context *gc, struct plot_info *pi) +{ + setup_pp_limits(gc, pi); + + if (enabled_graphs.po2) { + plot_single_gas_pp_text(gc, pi, po2_value, 0.4, PO2); + } + if (enabled_graphs.pn2) { + plot_single_gas_pp_text(gc, pi, pn2_value, 0.4, PN2); + } + if (enabled_graphs.phe) { + plot_single_gas_pp_text(gc, pi, phe_value, 0.4, PHE); + } +} + static void plot_pp_gas_profile(struct graphics_context *gc, struct plot_info *pi) { int i; @@ -1591,8 +1850,10 @@ void plot(struct graphics_context *gc, cairo_rectangle_t *drawing_area, struct d cairo_close_path(gc->cr); cairo_stroke(gc->cr); - if (GRAPHS_ENABLED) + if (GRAPHS_ENABLED) { plot_pp_gas_profile(gc, pi); + plot_pp_text(gc, pi); + } /* now shift the translation back by half the margin; * this way we can draw the vertical scales on both sides */ -- cgit v1.2.3-70-g09d2 From 41930e1edd8f61fc4274cc4e8993393cfa837ab9 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Mon, 5 Nov 2012 20:13:05 -0800 Subject: Fix incorrect variable reuse bug in preferences dialog This was introduced in commit ee803ef5d85b "Change preferences into a notebook and add second page for tec settings" In order to be able to get the new default XML file name back from the button, we need to keep that variable around and not overwrite it with more buttons for the tec preferences page. Reported-by: Linus Torvalds Signed-off-by: Dirk Hohndel --- gtk-gui.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/gtk-gui.c b/gtk-gui.c index e5b35f468..b14abb75b 100644 --- a/gtk-gui.c +++ b/gtk-gui.c @@ -568,7 +568,7 @@ static void pick_default_file(GtkWidget *w, GtkButton *button) static void preferences_dialog(GtkWidget *w, gpointer data) { int result; - GtkWidget *dialog, *notebook, *font, *frame, *box, *vbox, *button; + GtkWidget *dialog, *notebook, *font, *frame, *box, *vbox, *button, *xmlfile_button; const char *current_default, *new_default; menu_units = output_units; @@ -677,9 +677,10 @@ static void preferences_dialog(GtkWidget *w, gpointer data) box = gtk_hbox_new(FALSE, 6); gtk_container_add(GTK_CONTAINER(frame), box); current_default = subsurface_default_filename(); - button = gtk_button_new_with_label(current_default); - g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(pick_default_file), button); - gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6); + xmlfile_button = gtk_button_new_with_label(current_default); + g_signal_connect(G_OBJECT(xmlfile_button), "clicked", + G_CALLBACK(pick_default_file), xmlfile_button); + gtk_box_pack_start(GTK_BOX(box), xmlfile_button, FALSE, FALSE, 6); /* vbox that holds the second notebook page */ vbox = gtk_vbox_new(FALSE, 6); @@ -755,7 +756,7 @@ static void preferences_dialog(GtkWidget *w, gpointer data) subsurface_set_conf("pn2graph", PREF_BOOL, BOOL_TO_PTR(enabled_graphs.pn2)); subsurface_set_conf("phegraph", PREF_BOOL, BOOL_TO_PTR(enabled_graphs.phe)); - new_default = strdup(gtk_button_get_label(GTK_BUTTON(button))); + new_default = strdup(gtk_button_get_label(GTK_BUTTON(xmlfile_button))); /* if we opened the default file and are changing its name, * update existing_filename */ -- cgit v1.2.3-70-g09d2 From d2edc681a4c5c5818ca3dfe103befcbfb25acb79 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Mon, 5 Nov 2012 20:44:51 -0800 Subject: Fine tune the vertical spacing of the graphs Whatever I pick here, there will be dives where the different graphs end up interfering with each other. I don't think there isn't an easy, generic solution for this (but I can envision awesome non-easy solutions - they just don't seem to be worth the effort). But for most dives that I played with this seems to work pretty well. Signed-off-by: Dirk Hohndel --- profile.c | 36 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/profile.c b/profile.c index 60adbc0a7..d75b24f40 100644 --- a/profile.c +++ b/profile.c @@ -242,7 +242,7 @@ static int get_maxdepth(struct plot_info *pi) if (md <= 20000) md += 10000; else - md += ROUND_UP(md / 3, 10000); + md += ROUND_UP(md / 2, 10000); } return md; } @@ -684,16 +684,17 @@ static void setup_pp_limits(struct graphics_context *gc, struct plot_info *pi) gc->rightx = get_maxtime(pi); /* the maxdepth already includes extra vertical space - and if - * we take the corresponding pressure as maximum partial + * we use 1.5 times the corresponding pressure as maximum partial * pressure the graph seems to look fine*/ maxdepth = get_maxdepth(pi); - gc->topy = (maxdepth + 10000) / 10000.0 * 1.01325; + gc->topy = 1.5 * (maxdepth + 10000) / 10000.0 * 1.01325; gc->bottomy = 0.0; } -static void plot_single_pp_text(struct graphics_context *gc, int sec, double pp, color_indice_t color) +static void plot_single_pp_text(struct graphics_context *gc, int sec, double pp, + double vpos, color_indice_t color) { - text_render_options_t tro = {12, color, CENTER, BOTTOM}; + text_render_options_t tro = {12, color, CENTER, vpos}; plot_text(gc, &tro, sec, pp, "%.1lf", pp); if (color == PN2) @@ -743,7 +744,7 @@ static void plot_single_gas_pp_text(struct graphics_context *gc, struct plot_inf #if DEBUG_PROFILE > 1 fprintf(debugfile, "POI at %d sec value %lf\n", entry->sec, entry->po2); #endif - plot_single_pp_text(gc, entry->sec, value_func(pois[i], pi), color); + plot_single_pp_text(gc, entry->sec, value_func(pois[i], pi), pois_vpos[i], color); } free(pois); free(pois_vpos); @@ -757,7 +758,7 @@ static void plot_pp_text(struct graphics_context *gc, struct plot_info *pi) plot_single_gas_pp_text(gc, pi, po2_value, 0.4, PO2); } if (enabled_graphs.pn2) { - plot_single_gas_pp_text(gc, pi, pn2_value, 0.4, PN2); + plot_single_gas_pp_text(gc, pi, pn2_value, 0.6, PN2); } if (enabled_graphs.phe) { plot_single_gas_pp_text(gc, pi, phe_value, 0.4, PHE); @@ -767,18 +768,9 @@ static void plot_pp_text(struct graphics_context *gc, struct plot_info *pi) static void plot_pp_gas_profile(struct graphics_context *gc, struct plot_info *pi) { int i; - int maxdepth; struct plot_data *entry; - gc->leftx = 0; - gc->rightx = get_maxtime(pi); - - /* the maxdepth already includes extra vertical space - and if - * we take the corresponding pressure as maximum partial - * pressure the graph seems to look fine*/ - maxdepth = get_maxdepth(pi); - gc->topy = (maxdepth + 10000) / 10000.0 * 1.01325; - gc->bottomy = 0.0; + setup_pp_limits(gc, pi); if (enabled_graphs.po2) { set_source_rgba(gc, PO2); @@ -953,9 +945,9 @@ static int setup_temperature_limits(struct graphics_context *gc, struct plot_inf gc->topy = maxtemp + 1500 + delta*2; if (GRAPHS_ENABLED) - gc->bottomy = mintemp - delta * 1.5; + gc->bottomy = mintemp - delta * 2; else - gc->bottomy = mintemp - delta/2; + gc->bottomy = mintemp - delta / 2; return maxtemp > mintemp; } @@ -1043,7 +1035,11 @@ static int get_cylinder_pressure_range(struct graphics_context *gc, struct plot_ gc->leftx = 0; gc->rightx = get_maxtime(pi); - gc->bottomy = 0; gc->topy = pi->maxpressure * 1.5; + if (GRAPHS_ENABLED) + gc->bottomy = -pi->maxpressure * 0.75; + else + gc->bottomy = 0; + gc->topy = pi->maxpressure * 1.5; return pi->maxpressure != 0; } -- cgit v1.2.3-70-g09d2 From af939ee4c882ed08bd5a3f7a72bb5698deb36630 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Mon, 5 Nov 2012 20:51:41 -0800 Subject: Be smarter about printing gas partial pressure text We always want to print absolute maxima and minima - but not when multiple consecutive data points all have the same value (this happens, for example, when printing a pHe plot on non-helium dives - or when the dive profile includes a brief surface intervall which causes all the partial pressures to be at their minimum). Signed-off-by: Dirk Hohndel --- profile.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/profile.c b/profile.c index d75b24f40..faed6c0d3 100644 --- a/profile.c +++ b/profile.c @@ -656,9 +656,9 @@ static void find_points_of_interest(struct plot_info *pi, double (*value_func)(i data_max[i] = data[j]; } /* is i the overall minimum or maximum */ - if (data[i] == max) + if (data[i] == max && (i == 0 || data[i - 1] != max)) add_index(i, deltax, poip, poip_vpos, BOTTOM); - if (data[i] == min) + if (data[i] == min && (i == 0 || data[i - 1] != min)) add_index(i, deltax, poip, poip_vpos, TOP); /* is i a spike? */ if (fabs(spk_data[i]) > 0.01 && ! higher_spike(spk_data, i, nr, deltax)) { -- cgit v1.2.3-70-g09d2 From 1218e7a5e9fc094b6da7e3dde3d3b8578ee6bfab Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Mon, 5 Nov 2012 20:59:25 -0800 Subject: Don't print mean depth across the whole profile area I can't remember why we initially did this instead of ending the horizontal red line whith the last data point of the pressure profile. But especially nuw with more graphs shown the one line that extends past the end of the dive looked really silly. Signed-off-by: Dirk Hohndel --- profile.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/profile.c b/profile.c index faed6c0d3..65f1dab93 100644 --- a/profile.c +++ b/profile.c @@ -871,16 +871,16 @@ static void plot_depth_profile(struct graphics_context *gc, struct plot_info *pi } cairo_stroke(cr); + gc->leftx = 0; gc->rightx = maxtime; + /* Show mean depth */ if (! gc->printer) { set_source_rgba(gc, MEAN_DEPTH); move_to(gc, 0, pi->meandepth); - line_to(gc, 1, pi->meandepth); + line_to(gc, pi->entry[pi->nr - 1].sec, pi->meandepth); cairo_stroke(cr); } - gc->leftx = 0; gc->rightx = maxtime; - /* * These are good for debugging text placement etc, * but not for actual display.. -- cgit v1.2.3-70-g09d2 From 0d33337a2036e494a6d1005c5c738eb54973d37b Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Tue, 6 Nov 2012 06:41:34 +0100 Subject: Remove pN2 debug printout and add disabled code for partial pressure scale The pN2 print shouldn't have been committed, but I don't want to try and rewrite all the commit history. Oh well. The pressure scale I am ambivalent about. It seems that it should be useful - but that would require guide lines that coincide with the values which would really throw off the visual for me. So I added the code, but left it disabled. Signed-off-by: Dirk Hohndel --- profile.c | 38 ++++++++++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/profile.c b/profile.c index 65f1dab93..419d5a653 100644 --- a/profile.c +++ b/profile.c @@ -696,9 +696,6 @@ static void plot_single_pp_text(struct graphics_context *gc, int sec, double pp, { text_render_options_t tro = {12, color, CENTER, vpos}; plot_text(gc, &tro, sec, pp, "%.1lf", pp); - - if (color == PN2) - printf("pN2 %lf\n", pp); } #define MAXPP(_mpp, _pp) { _mpp = 0; \ @@ -722,12 +719,14 @@ static double phe_value(int idx, struct plot_info *pi) return pi->entry[idx].phe; } -static void plot_single_gas_pp_text(struct graphics_context *gc, struct plot_info *pi, +static double plot_single_gas_pp_text(struct graphics_context *gc, struct plot_info *pi, double (*value_func)(int, struct plot_info *), double value_threshold, int color) { int *pois, *pois_vpos; int i, two_minutes = 1; + double maxpp = 0.0; + /* don't bother with local min/max if the dive is under two minutes */ if (pi->entry[pi->nr - 1].sec > 120) { int idx = 0; @@ -741,27 +740,50 @@ static void plot_single_gas_pp_text(struct graphics_context *gc, struct plot_inf find_points_of_interest(pi, value_func, two_minutes, value_threshold, &pois, &pois_vpos); for (i = 0; pois[i] != -1; i++) { struct plot_data *entry = pi->entry + pois[i]; + double value = value_func(pois[i], pi); + #if DEBUG_PROFILE > 1 fprintf(debugfile, "POI at %d sec value %lf\n", entry->sec, entry->po2); #endif - plot_single_pp_text(gc, entry->sec, value_func(pois[i], pi), pois_vpos[i], color); + plot_single_pp_text(gc, entry->sec, value, pois_vpos[i], color); + if (value > maxpp) + maxpp = value; } free(pois); free(pois_vpos); + + return maxpp; } static void plot_pp_text(struct graphics_context *gc, struct plot_info *pi) { + double pp, dpp, m, maxpp = 0.0; + int hpos; + static const text_render_options_t tro = {11, PN2, LEFT, MIDDLE}; + setup_pp_limits(gc, pi); if (enabled_graphs.po2) { - plot_single_gas_pp_text(gc, pi, po2_value, 0.4, PO2); + maxpp = plot_single_gas_pp_text(gc, pi, po2_value, 0.4, PO2); } if (enabled_graphs.pn2) { - plot_single_gas_pp_text(gc, pi, pn2_value, 0.6, PN2); + m = plot_single_gas_pp_text(gc, pi, pn2_value, 0.6, PN2); + if (m > maxpp) + maxpp = m; } if (enabled_graphs.phe) { - plot_single_gas_pp_text(gc, pi, phe_value, 0.4, PHE); + m = plot_single_gas_pp_text(gc, pi, phe_value, 0.4, PHE); + if (m > maxpp) + maxpp = m; + } + /* while this is somewhat useful, I don't like the way it looks... + * for now I'll leave the code here, but disable it */ + if (0) { + pp = floor(maxpp * 10.0) / 10.0 + 0.2; + dpp = floor(2.0 * pp) / 10.0; + hpos = pi->entry[pi->nr - 1].sec + 30; + for (m = 0.0; m <= pp; m += dpp) + plot_text(gc, &tro, hpos, m, "%.1f", m); } } -- cgit v1.2.3-70-g09d2