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) \
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
--- /dev/null
+#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);
+}
#ifndef DIVE_H
#define DIVE_H
+#include <stdlib.h>
+#include <time.h>
+
/*
* Some silly typedefs to make our units very explicit.
*
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 */
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
dive_table.dives = dives;
dive_table.allocated = allocated;
}
- dives[nr] = dive;
+ dives[nr] = fixup_dive(dive);
dive_table.nr = nr+1;
}
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++;
}
/* 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);
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)));
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;
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);