From: Linus Torvalds Date: Sat, 3 Sep 2011 20:19:26 +0000 (-0700) Subject: Add various dive fixups, and show pressure (if any) in the plot X-Git-Url: http://git.tdb.fi/?a=commitdiff_plain;h=1e75ceac0dbbf6a6eef1e13f076c3ae6a7af4c55;p=ext%2Fsubsurface.git Add various dive fixups, and show pressure (if any) in the plot Now the dive profile plot *really* needs some units. The pressure is just a random line otherwise. Signed-off-by: Linus Torvalds --- diff --git a/Makefile b/Makefile index 6e6732e..b16d38c 100644 --- 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 index 0000000..2ecd10c --- /dev/null +++ b/dive.c @@ -0,0 +1,216 @@ +#include +#include + +#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 eaafe8a..e54d3a8 100644 --- a/dive.h +++ b/dive.h @@ -1,6 +1,9 @@ #ifndef DIVE_H #define DIVE_H +#include +#include + /* * 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 96149d3..5a047bb 100644 --- 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 diff --git a/parse-xml.c b/parse-xml.c index 3a84e29..e6ace21 100644 --- a/parse-xml.c +++ b/parse-xml.c @@ -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++; } diff --git a/profile.c b/profile.c index 9d84ff2..b4b9230 100644 --- 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);