]> git.tdb.fi Git - ext/subsurface.git/commitdiff
Add various dive fixups, and show pressure (if any) in the plot
authorLinus Torvalds <torvalds@linux-foundation.org>
Sat, 3 Sep 2011 20:19:26 +0000 (13:19 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sat, 3 Sep 2011 20:19:26 +0000 (13:19 -0700)
Now the dive profile plot *really* needs some units.  The pressure is
just a random line otherwise.

Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Makefile
dive.c [new file with mode: 0644]
dive.h
main.c
parse-xml.c
profile.c

index 6e6732e6e8f2988a03960949b9f6b740a0452c54..b16d38c7a5cd61145ebc8a9b2c2ff045e3d0d403 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
 CC=gcc
 CFLAGS=-Wall -Wno-pointer-sign -g
 
-OBJS=main.o profile.o info.o divelist.o parse-xml.o save-xml.o
+OBJS=main.o dive.o profile.o info.o divelist.o parse-xml.o save-xml.o
 
 divelog: $(OBJS)
        $(CC) $(LDLAGS) -o divelog $(OBJS) \
@@ -14,6 +14,9 @@ parse-xml.o: parse-xml.c dive.h
 save-xml.o: save-xml.c dive.h
        $(CC) $(CFLAGS) -c save-xml.c
 
+dive.o: dive.c dive.h
+       $(CC) $(CFLAGS) -c dive.c
+
 main.o: main.c dive.h display.h
        $(CC) $(CFLAGS) `pkg-config --cflags gtk+-2.0` -c main.c
 
diff --git a/dive.c b/dive.c
new file mode 100644 (file)
index 0000000..2ecd10c
--- /dev/null
+++ b/dive.c
@@ -0,0 +1,216 @@
+#include <string.h>
+#include <stdio.h>
+
+#include "dive.h"
+
+struct dive *fixup_dive(struct dive *dive)
+{
+       int i;
+       double depthtime = 0;
+       int lasttime = 0;
+       int start = -1, end = -1;
+       int startpress = 0, endpress = 0;
+       int starttemp = 0, endtemp = 0;
+       int maxdepth = 0, mintemp = 0;
+       int lastdepth = 0;
+
+       for (i = 0; i < dive->samples; i++) {
+               struct sample *sample = dive->sample + i;
+               int time = sample->time.seconds;
+               int depth = sample->depth.mm;
+               int press = sample->tankpressure.mbar;
+               int temp = sample->temperature.mkelvin;
+
+               if (lastdepth)
+                       end = time;
+
+               if (depth) {
+                       if (start < 0)
+                               start = lasttime;
+                       if (depth > maxdepth)
+                               maxdepth = depth;
+               }
+               if (press) {
+                       endpress = press;
+                       if (!startpress)
+                               startpress = press;
+               }
+               if (temp) {
+                       endtemp = temp;
+                       if (!starttemp)
+                               starttemp = temp;
+                       if (!mintemp || temp < mintemp)
+                               mintemp = temp;
+               }
+               depthtime += (time - lasttime) * (lastdepth + depth) / 2;
+               lastdepth = depth;
+               lasttime = time;
+       }
+       if (end < 0)
+               return dive;
+       dive->duration.seconds = end - start;
+       if (start != end)
+               dive->meandepth.mm = depthtime / (end - start);
+       if (startpress)
+               dive->beginning_pressure.mbar = startpress;
+       if (endpress)
+               dive->end_pressure.mbar = endpress;
+       if (mintemp)
+               dive->watertemp.mkelvin = mintemp;
+       if (maxdepth)
+               dive->maxdepth.mm = maxdepth;
+
+       return dive;
+}
+
+/* Don't pick a zero for MERGE_MIN() */
+#define MIN(a,b) ((a)<(b)?(a):(b))
+#define MAX(a,b) ((a)>(b)?(a):(b))
+#define MERGE_MAX(res, a, b, n) res->n = MAX(a->n, b->n)
+#define MERGE_MIN(res, a, b, n) res->n = (a->n)?(b->n)?MIN(a->n, b->n):(a->n):(b->n)
+
+static int alloc_samples;
+
+static struct dive *add_sample(struct sample *sample, int time, struct dive *dive)
+{
+       int nr = dive->samples;
+       struct sample *d;
+
+       if (nr >= alloc_samples) {
+               alloc_samples = (alloc_samples + 64) * 3 / 2;
+               dive = realloc(dive, dive_size(alloc_samples));
+               if (!dive)
+                       return NULL;
+       }
+       dive->samples = nr+1;
+       d = dive->sample + nr;
+
+       *d = *sample;
+       d->time.seconds = time;
+       return dive;
+}
+
+/*
+ * Merge samples. Dive 'a' is "offset" seconds before Dive 'b'
+ */
+static struct dive *merge_samples(struct dive *res, struct dive *a, struct dive *b, int offset)
+{
+       int asamples = a->samples;
+       int bsamples = b->samples;
+       struct sample *as = a->sample;
+       struct sample *bs = b->sample;
+
+       for (;;) {
+               int at, bt;
+               struct sample sample;
+
+               if (!res)
+                       return NULL;
+
+               at = asamples ? as->time.seconds : -1;
+               bt = bsamples ? bs->time.seconds + offset : -1;
+
+               /* No samples? All done! */
+               if (at < 0 && bt < 0)
+                       return fixup_dive(res);
+
+               /* Only samples from a? */
+               if (bt < 0) {
+add_sample_a:
+                       res = add_sample(as, at, res);
+                       as++;
+                       asamples--;
+                       continue;
+               }
+
+               /* Only samples from b? */
+               if (at < 0) {
+add_sample_b:
+                       res = add_sample(bs, bt, res);
+                       bs++;
+                       bsamples--;
+                       continue;
+               }
+
+               if (at < bt)
+                       goto add_sample_a;
+               if (at > bt)
+                       goto add_sample_b;
+
+               /* same-time sample: add a merged sample. Take the non-zero ones */
+               sample = *bs;
+               if (as->depth.mm)
+                       sample.depth = as->depth;
+               if (as->temperature.mkelvin)
+                       sample.temperature = as->temperature;
+               if (as->tankpressure.mbar)
+                       sample.tankpressure = as->tankpressure;
+               if (as->tankindex)
+                       sample.tankindex = as->tankindex;
+
+               res = add_sample(&sample, at, res);
+
+               as++;
+               bs++;
+               asamples--;
+               bsamples--;
+       }
+}
+
+static char *merge_text(const char *a, const char *b)
+{
+       char *res;
+
+       if (!a || !*a)
+               return (char *)b;
+       if (!b || !*b)
+               return (char *)a;
+       if (!strcmp(a,b))
+               return (char *)a;
+       res = malloc(strlen(a) + strlen(b) + 9);
+       if (!res)
+               return (char *)a;
+       sprintf(res, "(%s) or (%s)", a, b);
+       return res;
+}
+
+/*
+ * This could do a lot more merging. Right now it really only
+ * merges almost exact duplicates - something that happens easily
+ * with overlapping dive downloads.
+ */
+struct dive *try_to_merge(struct dive *a, struct dive *b)
+{
+       int i;
+       struct dive *res;
+
+       if (a->when != b->when)
+               return NULL;
+
+       alloc_samples = 5;
+       res = malloc(dive_size(alloc_samples));
+       if (!res)
+               return NULL;
+       memset(res, 0, dive_size(alloc_samples));
+
+       res->when = a->when;
+       res->name = merge_text(a->name, b->name);
+       res->location = merge_text(a->location, b->location);
+       res->notes = merge_text(a->notes, b->notes);
+       MERGE_MAX(res, a, b, maxdepth.mm);
+       res->meandepth.mm = 0;
+       MERGE_MAX(res, a, b, duration.seconds);
+       MERGE_MAX(res, a, b, surfacetime.seconds);
+       MERGE_MAX(res, a, b, airtemp.mkelvin);
+       MERGE_MIN(res, a, b, watertemp.mkelvin);
+       MERGE_MAX(res, a, b, beginning_pressure.mbar);
+       MERGE_MAX(res, a, b, end_pressure.mbar);
+       for (i = 0; i < MAX_MIXES; i++) {
+               if (a->gasmix[i].o2.permille) {
+                       res->gasmix[i] = a->gasmix[i];
+                       continue;
+               }
+               res->gasmix[i] = b->gasmix[i];
+       }
+       return merge_samples(res, a, b, 0);
+}
diff --git a/dive.h b/dive.h
index eaafe8aeba7f4209719c2d079f4461067d4d1ef1..e54d3a8624361e7cb9a1085a35c576cf62bf3536 100644 (file)
--- a/dive.h
+++ b/dive.h
@@ -1,6 +1,9 @@
 #ifndef DIVE_H
 #define DIVE_H
 
+#include <stdlib.h>
+#include <time.h>
+
 /*
  * Some silly typedefs to make our units very explicit.
  *
@@ -143,4 +146,7 @@ static inline unsigned int dive_size(int samples)
        return sizeof(struct dive) + samples*sizeof(struct sample);
 }
 
+extern struct dive *fixup_dive(struct dive *dive);
+extern struct dive *try_to_merge(struct dive *a, struct dive *b);
+
 #endif /* DIVE_H */
diff --git a/main.c b/main.c
index 96149d38c870d75f62163edf5c705d9f2763d65e..5a047bb09a58882244235f6838a409c8ce4e5cff 100644 (file)
--- a/main.c
+++ b/main.c
@@ -20,156 +20,6 @@ static int sortfn(const void *_a, const void *_b)
        return 0;
 }
 
-static int alloc_samples;
-
-/* Don't pick a zero for MERGE_MIN() */
-#define MERGE_MAX(res, a, b, n) res->n = MAX(a->n, b->n)
-#define MERGE_MIN(res, a, b, n) res->n = (a->n)?(b->n)?MIN(a->n, b->n):(a->n):(b->n)
-
-static struct dive *add_sample(struct sample *sample, int time, struct dive *dive)
-{
-       int nr = dive->samples;
-       struct sample *d;
-
-       if (nr >= alloc_samples) {
-               alloc_samples = (alloc_samples + 64) * 3 / 2;
-               dive = realloc(dive, dive_size(alloc_samples));
-               if (!dive)
-                       return NULL;
-       }
-       dive->samples = nr+1;
-       d = dive->sample + nr;
-
-       *d = *sample;
-       d->time.seconds = time;
-       return dive;
-}
-
-/*
- * Merge samples. Dive 'a' is "offset" seconds before Dive 'b'
- */
-static struct dive *merge_samples(struct dive *res, struct dive *a, struct dive *b, int offset)
-{
-       int asamples = a->samples;
-       int bsamples = b->samples;
-       struct sample *as = a->sample;
-       struct sample *bs = b->sample;
-
-       for (;;) {
-               int at, bt;
-               struct sample sample;
-
-               if (!res)
-                       return NULL;
-
-               at = asamples ? as->time.seconds : -1;
-               bt = bsamples ? bs->time.seconds + offset : -1;
-
-               /* No samples? All done! */
-               if (at < 0 && bt < 0)
-                       return res;
-
-               /* Only samples from a? */
-               if (bt < 0) {
-add_sample_a:
-                       res = add_sample(as, at, res);
-                       as++;
-                       asamples--;
-                       continue;
-               }
-
-               /* Only samples from b? */
-               if (at < 0) {
-add_sample_b:
-                       res = add_sample(bs, bt, res);
-                       bs++;
-                       bsamples--;
-                       continue;
-               }
-
-               if (at < bt)
-                       goto add_sample_a;
-               if (at > bt)
-                       goto add_sample_b;
-
-               /* same-time sample: add a merged sample. Take the non-zero ones */
-               sample = *bs;
-               if (as->depth.mm)
-                       sample.depth = as->depth;
-               if (as->temperature.mkelvin)
-                       sample.temperature = as->temperature;
-               if (as->tankpressure.mbar)
-                       sample.tankpressure = as->tankpressure;
-               if (as->tankindex)
-                       sample.tankindex = as->tankindex;
-
-               res = add_sample(&sample, at, res);
-
-               as++;
-               bs++;
-               asamples--;
-               bsamples--;
-       }
-}
-
-static char *merge_text(const char *a, const char *b)
-{
-       char *res;
-
-       if (!a || !*a)
-               return (char *)b;
-       if (!b || !*b)
-               return (char *)a;
-       if (!strcmp(a,b))
-               return (char *)a;
-       res = malloc(strlen(a) + strlen(b) + 9);
-       if (!res)
-               return (char *)a;
-       sprintf(res, "(%s) or (%s)", a, b);
-       return res;
-}
-
-/*
- * This could do a lot more merging. Right now it really only
- * merges almost exact duplicates - something that happens easily
- * with overlapping dive downloads.
- */
-static struct dive *try_to_merge(struct dive *a, struct dive *b)
-{
-       int i;
-       struct dive *res;
-
-       if (a->when != b->when)
-               return NULL;
-
-       alloc_samples = 5;
-       res = malloc(dive_size(alloc_samples));
-       if (!res)
-               return NULL;
-       memset(res, 0, dive_size(alloc_samples));
-
-       res->when = a->when;
-       res->name = merge_text(a->name, b->name);
-       res->location = merge_text(a->location, b->location);
-       res->notes = merge_text(a->notes, b->notes);
-       MERGE_MAX(res, a, b, maxdepth.mm);
-       MERGE_MAX(res, a, b, meandepth.mm);     /* recalc! */
-       MERGE_MAX(res, a, b, duration.seconds);
-       MERGE_MAX(res, a, b, surfacetime.seconds);
-       MERGE_MAX(res, a, b, airtemp.mkelvin);
-       MERGE_MIN(res, a, b, watertemp.mkelvin);
-       MERGE_MAX(res, a, b, beginning_pressure.mbar);
-       MERGE_MAX(res, a, b, end_pressure.mbar);
-       for (i = 0; i < MAX_MIXES; i++) {
-               if (a->gasmix[i].o2.permille) {
-                       res->gasmix[i] = a->gasmix[i];
-                       continue;
-               }
-               res->gasmix[i] = b->gasmix[i];
-       }
-       return merge_samples(res, a, b, 0);
-}
-
 /*
  * This doesn't really report anything at all. We just sort the
  * dives, the GUI does the reporting
index 3a84e296321d06b1453f86c2d868f78ff5b4cc8e..e6ace216fb7c17336f3f4e9456dc259ba5d55469 100644 (file)
@@ -29,7 +29,7 @@ static void record_dive(struct dive *dive)
                dive_table.dives = dives;
                dive_table.allocated = allocated;
        }
-       dives[nr] = dive;
+       dives[nr] = fixup_dive(dive);
        dive_table.nr = nr+1;
 }
 
@@ -787,19 +787,6 @@ static void sample_end(void)
        if (!dive)
                return;
 
-       if (sample->time.seconds > dive->duration.seconds) {
-               if (sample->depth.mm)
-                       dive->duration = sample->time;
-       }
-
-       if (sample->depth.mm > dive->maxdepth.mm)
-               dive->maxdepth.mm = sample->depth.mm;
-
-       if (sample->temperature.mkelvin) {
-               if (!dive->watertemp.mkelvin || dive->watertemp.mkelvin > sample->temperature.mkelvin)
-                       dive->watertemp = sample->temperature;
-       }
-
        sample = NULL;
        dive->samples++;
 }
index 9d84ff29d64de926983555be28660abcef2786dc..b4b923095864a5a5736f901c3781d2dae6ac3ada 100644 (file)
--- a/profile.c
+++ b/profile.c
@@ -28,18 +28,18 @@ static int round_feet_up(int feet)
 /* Scale to 0,0 -> maxx,maxy */
 #define SCALE(x,y) (x)*maxx/scalex+topx,(y)*maxy/scaley+topy
 
-static void plot(cairo_t *cr, int w, int h, struct dive *dive, int samples, struct sample *sample)
+static void plot_profile(struct dive *dive, cairo_t *cr,
+       double topx, double topy, double maxx, double maxy)
 {
-       int i;
-       double topx, topy, maxx, maxy;
        double scalex, scaley;
-       int maxtime, maxdepth;
        int begins, sec, depth;
+       int i, samples;
+       struct sample *sample;
+       int maxtime, maxdepth;
 
-       topx = w / 20.0;
-       topy = h / 20.0;
-       maxx = (w - 2*topx);
-       maxy = (h - 2*topy);
+       samples = dive->samples;
+       if (!samples)
+               return;
 
        cairo_set_line_width(cr, 2);
 
@@ -67,7 +67,7 @@ static void plot(cairo_t *cr, int w, int h, struct dive *dive, int samples, stru
 
        scaley = maxdepth;
 
-       /* Depth profile */
+       sample = dive->sample;
        cairo_set_source_rgba(cr, 1, 0.2, 0.2, 0.80);
        begins = sample->time.seconds;
        cairo_move_to(cr, SCALE(sample->time.seconds, to_feet(sample->depth)));
@@ -85,6 +85,76 @@ static void plot(cairo_t *cr, int w, int h, struct dive *dive, int samples, stru
        cairo_fill_preserve(cr);
        cairo_set_source_rgba(cr, 1, 0.2, 0.2, 0.80);
        cairo_stroke(cr);
+}
+
+static int get_tank_pressure_range(struct dive *dive, double *scalex, double *scaley)
+{
+       int i;
+       double min, max;
+
+       *scalex = round_seconds_up(dive->duration.seconds);
+
+       max = 0;
+       min = 5000;
+       for (i = 0; i < dive->samples; i++) {
+               struct sample *sample = dive->sample + i;
+               double bar;
+
+               if (!sample->tankpressure.mbar)
+                       continue;
+               bar = sample->tankpressure.mbar;
+               if (bar < min)
+                       min = bar;
+               if (bar > max)
+                       max = bar;
+       }
+       if (!max)
+               return 0;
+       *scaley = max * 1.5;
+       return 1;
+}
+
+static void plot_tank_pressure(struct dive *dive, cairo_t *cr,
+       double topx, double topy, double maxx, double maxy)
+{
+       int i;
+       double scalex, scaley;
+
+       if (!get_tank_pressure_range(dive, &scalex, &scaley))
+               return;
+
+       cairo_set_source_rgba(cr, 0.2, 1.0, 0.2, 0.80);
+
+       cairo_move_to(cr, SCALE(0, dive->beginning_pressure.mbar));
+       for (i = 1; i < dive->samples; i++) {
+               int sec, mbar;
+               struct sample *sample = dive->sample + i;
+
+               sec = sample->time.seconds;
+               mbar = sample->tankpressure.mbar;
+               if (!mbar)
+                       continue;
+               cairo_line_to(cr, SCALE(sec, mbar));
+       }
+       cairo_line_to(cr, SCALE(dive->duration.seconds, dive->end_pressure.mbar));
+       cairo_stroke(cr);
+}
+
+static void plot(cairo_t *cr, int w, int h, struct dive *dive)
+{
+       double topx, topy, maxx, maxy;
+       double scalex, scaley;
+
+       topx = w / 20.0;
+       topy = h / 20.0;
+       maxx = (w - 2*topx);
+       maxy = (h - 2*topy);
+
+       /* Depth profile */
+       plot_profile(dive, cr, topx, topy, maxx, maxy);
+
+       /* Tank pressure plot? */
+       plot_tank_pressure(dive, cr, topx, topy, maxx, maxy);
 
        /* Bounding box last */
        scalex = scaley = 1.0;
@@ -111,8 +181,8 @@ static gboolean expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer
        cairo_set_source_rgb(cr, 0, 0, 0);
        cairo_paint(cr);
 
-       if (dive && dive->samples)
-               plot(cr, w, h, dive, dive->samples, dive->sample);
+       if (dive)
+               plot(cr, w, h, dive);
 
        cairo_destroy(cr);