summaryrefslogtreecommitdiffstats
path: root/statistics.c
diff options
context:
space:
mode:
authorGravatar Linus Torvalds <torvalds@linux-foundation.org>2011-11-02 12:39:55 -0700
committerGravatar Linus Torvalds <torvalds@linux-foundation.org>2011-11-02 12:39:55 -0700
commit55352a051cfa7ac519e397cd3d18851e5050517b (patch)
tree8d42a09d66212b84f992c8d3ce6b8e341cdac94a /statistics.c
parente4bfb6597279ca3d9aa3678a617f5f0aef298278 (diff)
parent619ab9e828d4db7b0c4089018b09892c9d04ece9 (diff)
downloadsubsurface-55352a051cfa7ac519e397cd3d18851e5050517b.tar.gz
Merge branch 'add-info-stats-page' of git://github.com/dirkhh/subsurface
* 'add-info-stats-page' of git://github.com/dirkhh/subsurface: Add Info & Stats page to the notebook Even more places with pressure and volume conversions Further cleanup of pressure and volume conversions Use unit functions to get column headers, add unit function for pressure More consistency improvements Add new helper function to get temperature and unit
Diffstat (limited to 'statistics.c')
-rw-r--r--statistics.c277
1 files changed, 277 insertions, 0 deletions
diff --git a/statistics.c b/statistics.c
new file mode 100644
index 000000000..845bc2a03
--- /dev/null
+++ b/statistics.c
@@ -0,0 +1,277 @@
+/* statistics.c */
+/* creates the UI for the Info & Stats page -
+ * controlled through the following interfaces:
+ *
+ * void show_dive_stats(struct dive *dive)
+ * void flush_dive_stats_changes(struct dive *dive)
+ *
+ * called from gtk-ui:
+ * GtkWidget *stats_widget(void)
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <time.h>
+
+#include "dive.h"
+#include "display.h"
+#include "display-gtk.h"
+#include "divelist.h"
+
+typedef struct {
+ GtkWidget *date,
+ *dive_time,
+ *surf_intv,
+ *max_depth,
+ *avg_depth,
+ *water_temp,
+ *sac,
+ *otu,
+ *o2he,
+ *gas_used,
+ *total_time,
+ *avg_time,
+ *max_overall_depth,
+ *avg_overall_depth,
+ *min_sac,
+ *avg_sac,
+ *max_sac;
+} info_stat_widget_t;
+
+static info_stat_widget_t info_stat_w;
+
+typedef struct {
+ duration_t total_time;
+ /* avg_time is simply total_time / nr -- let's not keep this */
+ depth_t max_depth;
+ depth_t avg_depth;
+ volume_t max_sac;
+ volume_t min_sac;
+ volume_t avg_sac;
+} info_stat_t;
+
+static info_stat_t info_stat;
+
+static void process_all_dives(struct dive *dive, struct dive **prev_dive)
+{
+ int idx;
+ struct dive *dp;
+ int old_tt, sac_time = 0;
+
+ *prev_dive = NULL;
+ memset(&info_stat, 0, sizeof(info_stat));
+ /* this relies on the fact that the dives in the dive_table
+ * are in chronological order */
+ for (idx = 0; idx < dive_table.nr; idx++) {
+ dp = dive_table.dives[idx];
+ if (dp->when == dive->when) {
+ /* that's the one we are showing */
+ if (idx > 0)
+ *prev_dive = dive_table.dives[idx-1];
+ }
+ old_tt = info_stat.total_time.seconds;
+ info_stat.total_time.seconds += dp->duration.seconds;
+ if (dp->maxdepth.mm > info_stat.max_depth.mm)
+ info_stat.max_depth.mm = dp->maxdepth.mm;
+ info_stat.avg_depth.mm = (1.0 * old_tt * info_stat.avg_depth.mm +
+ dp->duration.seconds * dp->meandepth.mm) / info_stat.total_time.seconds;
+ if (dp->sac > 0) {
+ int old_sac_time = sac_time;
+ sac_time += dp->duration.seconds;
+ info_stat.avg_sac.mliter = (1.0 * old_sac_time * info_stat.avg_sac.mliter +
+ dp->duration.seconds * dp->sac) / sac_time ;
+ if (dp->sac > info_stat.max_sac.mliter)
+ info_stat.max_sac.mliter = dp->sac;
+ if (info_stat.min_sac.mliter == 0 || dp->sac < info_stat.max_sac.mliter)
+ info_stat.min_sac.mliter = dp->sac;
+ }
+ }
+}
+
+static void set_label(GtkWidget *w, const char *fmt, ...)
+{
+ char buf[80];
+ va_list args;
+
+ va_start(args, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, args);
+ va_end(args);
+ gtk_label_set_text(GTK_LABEL(w), buf);
+}
+
+static char * get_time_string(int seconds, int maxdays)
+{
+ static char buf[80];
+ if (maxdays && seconds > 3600 * 24 * maxdays)
+ snprintf(buf, sizeof(buf), "more than %d days", maxdays);
+ else {
+ int days = seconds / 3600 / 24;
+ int hours = (seconds - days * 3600 * 24) / 3600;
+ int minutes = (seconds - days * 3600 * 24 - hours * 3600) / 60;
+ if (days > 0)
+ snprintf(buf, sizeof(buf), "%dd %dh %dmin", days, hours, minutes);
+ else
+ snprintf(buf, sizeof(buf), "%dh %dmin", hours, minutes);
+ }
+ return buf;
+}
+
+void show_dive_stats(struct dive *dive)
+{
+ char buf[80];
+ double value;
+ int decimals;
+ const char *unit;
+ int idx, offset, gas_used;
+ struct dive *prev_dive;
+
+ process_all_dives(dive, &prev_dive);
+
+ strftime(buf, 80, "%a, %b %d, %Y, %k:%M", gmtime(&dive->when));
+ set_label(info_stat_w.date, buf);
+ set_label(info_stat_w.dive_time, "%d min", (dive->duration.seconds + 30) / 60);
+ if (prev_dive)
+ set_label(info_stat_w.surf_intv, get_time_string(dive->when - prev_dive->when, 4));
+ else
+ set_label(info_stat_w.surf_intv, "unknown");
+ value = get_depth_units(dive->maxdepth.mm, &decimals, &unit);
+ set_label(info_stat_w.max_depth, "%.*f %s", decimals, value, unit);
+ value = get_depth_units(dive->meandepth.mm, &decimals, &unit);
+ set_label(info_stat_w.avg_depth, "%.*f %s", decimals, value, unit);
+ value = get_temp_units(dive->watertemp.mkelvin, &unit);
+ set_label(info_stat_w.water_temp, "%.1f %s", value, unit);
+ value = get_volume_units(dive->sac, &decimals, &unit);
+ if (value > 0) {
+ set_label(info_stat_w.sac, "%.*f %s/min", decimals, value, unit);
+ } else
+ set_label(info_stat_w.sac, "");
+ set_label(info_stat_w.otu, "%d", dive->otu);
+ offset = 0;
+ gas_used = 0;
+ buf[0] = '\0';
+ /* for the O2/He readings just create a list of them */
+ for (idx = 0; idx < MAX_CYLINDERS; idx++) {
+ cylinder_t *cyl = &dive->cylinder[idx];
+ /* we assume that every valid cylinder has either a working pressure
+ * or a size; but for good measure let's also accept cylinders with
+ * a starting or ending pressure*/
+ if (cyl->type.workingpressure.mbar || cyl->type.size.mliter ||
+ cyl->start.mbar || cyl->end.mbar) {
+ /* 0% O2 strangely means air, so 21% - I don't like that at all */
+ int o2 = cyl->gasmix.o2.permille ? : 209;
+ if (offset > 0) {
+ snprintf(buf+offset, 80-offset, ", ");
+ offset += 2;
+ }
+ snprintf(buf+offset, 80-offset, "%d/%d", (o2 + 5) / 10,
+ (cyl->gasmix.he.permille + 5) / 10);
+ offset = strlen(buf);
+ }
+ /* and if we have size, start and end pressure, we can
+ * calculate the total gas used */
+ if (cyl->type.size.mliter && cyl->start.mbar && cyl->end.mbar)
+ gas_used += cyl->type.size.mliter / 1000.0 *
+ (cyl->start.mbar - cyl->end.mbar);
+ }
+ if (offset)
+ set_label(info_stat_w.o2he, buf);
+ if (gas_used) {
+ value = get_volume_units(gas_used, &decimals, &unit);
+ set_label(info_stat_w.gas_used, "%.*f %s", decimals, value, unit);
+ } else
+ set_label(info_stat_w.gas_used, "");
+ /* and now do the statistics */
+ set_label(info_stat_w.total_time, get_time_string(info_stat.total_time.seconds, 0));
+ set_label(info_stat_w.avg_time, get_time_string(info_stat.total_time.seconds / dive_table.nr, 0));
+ value = get_depth_units(info_stat.max_depth.mm, &decimals, &unit);
+ set_label(info_stat_w.max_overall_depth, "%.*f %s", decimals, value, unit);
+ value = get_depth_units(info_stat.avg_depth.mm, &decimals, &unit);
+ set_label(info_stat_w.avg_overall_depth, "%.*f %s", decimals, value, unit);
+ value = get_volume_units(info_stat.max_sac.mliter, &decimals, &unit);
+ set_label(info_stat_w.max_sac, "%.*f %s/min", decimals, value, unit);
+ value = get_volume_units(info_stat.min_sac.mliter, &decimals, &unit);
+ set_label(info_stat_w.min_sac, "%.*f %s/min", decimals, value, unit);
+ value = get_volume_units(info_stat.avg_sac.mliter, &decimals, &unit);
+ set_label(info_stat_w.avg_sac, "%.*f %s/min", decimals, value, unit);
+}
+
+void flush_dive_stats_changes(struct dive *dive)
+{
+ /* We do nothing: we require the "Ok" button press */
+}
+
+static GtkWidget *new_info_label_in_frame(GtkWidget *box, const char *label)
+{
+ GtkWidget *label_widget;
+ GtkWidget *frame;
+
+ frame = gtk_frame_new(label);
+ label_widget = gtk_label_new(NULL);
+ gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
+ gtk_container_add(GTK_CONTAINER(frame), label_widget);
+
+ return label_widget;
+}
+
+GtkWidget *stats_widget(void)
+{
+
+ GtkWidget *vbox, *hbox, *infoframe, *statsframe, *framebox;
+
+ vbox = gtk_vbox_new(FALSE, 3);
+
+ infoframe = gtk_frame_new("Dive Info");
+ gtk_box_pack_start(GTK_BOX(vbox), infoframe, TRUE, FALSE, 3);
+ framebox = gtk_vbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(infoframe), framebox);
+
+ /* first row */
+ hbox = gtk_hbox_new(FALSE, 3);
+ gtk_box_pack_start(GTK_BOX(framebox), hbox, TRUE, FALSE, 3);
+
+ info_stat_w.date = new_info_label_in_frame(hbox, "Date");
+ info_stat_w.dive_time = new_info_label_in_frame(hbox, "Dive Time");
+ info_stat_w.surf_intv = new_info_label_in_frame(hbox, "Surf Intv");
+
+ /* second row */
+ hbox = gtk_hbox_new(FALSE, 3);
+ gtk_box_pack_start(GTK_BOX(framebox), hbox, TRUE, FALSE, 3);
+
+ info_stat_w.max_depth = new_info_label_in_frame(hbox, "Max Depth");
+ info_stat_w.avg_depth = new_info_label_in_frame(hbox, "Avg Depth");
+ info_stat_w.water_temp = new_info_label_in_frame(hbox, "Water Temp");
+
+ /* third row */
+ hbox = gtk_hbox_new(FALSE, 3);
+ gtk_box_pack_start(GTK_BOX(framebox), hbox, TRUE, FALSE, 3);
+
+ info_stat_w.sac = new_info_label_in_frame(hbox, "SAC");
+ info_stat_w.otu = new_info_label_in_frame(hbox, "OTU");
+ info_stat_w.o2he = new_info_label_in_frame(hbox, "O" UTF8_SUBSCRIPT_2 " / He");
+ info_stat_w.gas_used = new_info_label_in_frame(hbox, "Gas Used");
+
+ statsframe = gtk_frame_new("Statistics");
+ gtk_box_pack_start(GTK_BOX(vbox), statsframe, TRUE, FALSE, 3);
+ framebox = gtk_vbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(statsframe), framebox);
+
+ /* first row */
+ hbox = gtk_hbox_new(FALSE, 3);
+ gtk_box_pack_start(GTK_BOX(framebox), hbox, TRUE, FALSE, 3);
+
+ info_stat_w.total_time = new_info_label_in_frame(hbox, "Total Time");
+ info_stat_w.avg_time = new_info_label_in_frame(hbox, "Avg Time");
+ info_stat_w.max_overall_depth = new_info_label_in_frame(hbox, "Max Depth");
+ info_stat_w.avg_overall_depth = new_info_label_in_frame(hbox, "Avg Depth");
+
+ /* second row */
+ hbox = gtk_hbox_new(FALSE, 3);
+ gtk_box_pack_start(GTK_BOX(framebox), hbox, TRUE, FALSE, 3);
+
+ info_stat_w.max_sac = new_info_label_in_frame(hbox, "Max SAC");
+ info_stat_w.min_sac = new_info_label_in_frame(hbox, "Min SAC");
+ info_stat_w.avg_sac = new_info_label_in_frame(hbox, "Avg SAC");
+
+ return vbox;
+}