diff options
author | Dirk Hohndel <dirk@hohndel.org> | 2012-11-10 11:56:28 +0100 |
---|---|---|
committer | Dirk Hohndel <dirk@hohndel.org> | 2012-11-10 12:02:33 +0100 |
commit | e29a43681049ef30eb5129f140a1ed0936a9b516 (patch) | |
tree | 4c57c2582e72e42266b7b51ec92276d1b8250347 | |
parent | 7f515eb7e53c5ab6d8dde4d2e6876464ed7fdeae (diff) | |
parent | 6ad73a8f043be283c07df34c6a5a43fee1b444e8 (diff) | |
download | subsurface-e29a43681049ef30eb5129f140a1ed0936a9b516.tar.gz |
Merge branch 'ceiling-plot'
This enables plotting the ceiling in deco dives and also adds the
necessary code to the uemis importer. The only other dive computer this
has been tested with the OSTC and that needs a libdivecomputer patch in
order to provide the deco/ceiling information to Subsurface.
Fixes #5
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | color.h | 1 | ||||
-rw-r--r-- | dive.c | 61 | ||||
-rw-r--r-- | dive.h | 2 | ||||
-rw-r--r-- | gtk-gui.c | 2 | ||||
-rw-r--r-- | parse-xml.c | 2 | ||||
-rw-r--r-- | profile.c | 105 | ||||
-rw-r--r-- | uemis-downloader.c | 10 | ||||
-rw-r--r-- | uemis.c | 23 |
9 files changed, 179 insertions, 29 deletions
@@ -252,7 +252,7 @@ gtk-gui.o: gtk-gui.c dive.h display.h divelist.h display-gtk.h libdivecomputer.h -c gtk-gui.c uemis.o: uemis.c dive.h uemis.h - $(CC) $(CFLAGS) $(GTK2CFLAGS) $(GLIB2CFLAGS) $(XML2CFLAGS) -c uemis.c + $(CC) $(CFLAGS) $(GTK2CFLAGS) $(GLIB2CFLAGS) $(XML2CFLAGS) $(LIBDIVECOMPUTERCFLAGS) -c uemis.c uemis-downloader.o: uemis-downloader.c dive.h uemis.h $(CC) $(CFLAGS) $(GTK2CFLAGS) $(GLIB2CFLAGS) $(XML2CFLAGS) -c uemis-downloader.c @@ -26,6 +26,7 @@ #define REDORANGE1 { 1.0, 0.2, 0.2, 1 } #define REDORANGE1_HIGH_TRANS { 1.0, 0.2, 0.2, 0.25 } #define REDORANGE1_MED_TRANS { 1.0, 0.2, 0.2, 0.5 } +#define RED1_MED_TRANS { 1.0, 0.0, 0.0, 0.5 } #define RED1 { 1.0, 0.0, 0.0, 1 } // Monochromes @@ -373,6 +373,30 @@ static void sanitize_cylinder_info(struct dive *dive) } } +/* some events should never be thrown away */ +static gboolean is_potentially_redundant(struct event *event) +{ + if (!strcmp(event->name, "gaschange")) + return FALSE; + return TRUE; +} + +/* match just by name - we compare the details in the code that uses this helper */ +static struct event *find_previous_event(struct dive *dive, struct event *event) +{ + struct event *ev = dive->events; + struct event *previous = NULL; + + if (!event->name) + return NULL; + while (ev && ev != event) { + if(ev->name && !strcmp(ev->name, event->name)) + previous = ev; + ev = ev->next; + } + return previous; +} + struct dive *fixup_dive(struct dive *dive) { int i,j; @@ -384,6 +408,7 @@ struct dive *fixup_dive(struct dive *dive) int lastdepth = 0; int lasttemp = 0, lastpressure = 0; int pressure_delta[MAX_CYLINDERS] = {INT_MAX, }; + struct event *event; add_people(dive->buddy); add_people(dive->divemaster); @@ -514,6 +539,42 @@ struct dive *fixup_dive(struct dive *dive) add_weightsystem_description(ws); } + /* events are stored as a linked list, so the concept of + * "consecutive, identical events" is somewhat hard to + * implement correctly (especially given that on some dive + * computers events are asynchronous, so they can come in + * between what would be the non-constant sample rate). + * + * So what we do is that we throw away clearly redundant + * events that are fewer than 61 seconds apart (assuming there + * is no dive computer with a sample rate of more than 60 + * seconds... that would be pretty pointless to plot the + * profile with) + * We first only mark the events for deletion so that we + * still know when the previous event happened. */ + event = dive->events; + while (event) { + struct event *prev; + if (is_potentially_redundant(event)) { + prev = find_previous_event(dive, event); + if (prev && prev->value == event->value && + prev->flags == event->flags && + event->time.seconds - prev->time.seconds < 61) + event->deleted = TRUE; + } + event = event->next; + } + event = dive->events; + while (event) { + if (event->next && event->next->deleted) { + struct event *nextnext = event->next->next; + free(event->next); + event->next = nextnext; + } else { + event = event->next; + } + } + return dive; } @@ -233,6 +233,7 @@ struct event { struct event *next; duration_t time; int type, flags, value; + gboolean deleted; char name[]; }; @@ -269,6 +270,7 @@ struct dive { duration_t duration, surfacetime; int visibility; /* 0 - 5 star rating */ temperature_t airtemp, watertemp; + pressure_t surface_pressure; cylinder_t cylinder[MAX_CYLINDERS]; weightsystem_t weightsystem[MAX_WEIGHTSYSTEMS]; char *suit; @@ -1231,7 +1231,7 @@ void attach_tooltip(int x, int y, int w, int h, const char *text) rect->y = y; rect->width = w; rect->height = h; - tooltip_rects[tooltips].text = text; + tooltip_rects[tooltips].text = strdup(text); tooltips++; } diff --git a/parse-xml.c b/parse-xml.c index 9429580e6..272b82c0f 100644 --- a/parse-xml.c +++ b/parse-xml.c @@ -545,7 +545,7 @@ static void decicelsius(char *buffer, void *_temp) temp->mkelvin = (val.fp/10 + 273.15) * 1000 + 0.5; break; default: - fprintf(stderr, "Strange julian date: %s", buffer); + fprintf(stderr, "Strange temperature: %s", buffer); } free(buffer); } @@ -15,6 +15,7 @@ #include "display-gtk.h" #include "divelist.h" #include "color.h" +#include "libdivecomputer/parser.h" int selected_dive = 0; char zoomed_plot = 0; @@ -44,6 +45,7 @@ struct plot_info { int temperature; /* Depth info */ int depth; + int ceiling; int smoothed; double po2, pn2, phe; velocity_t velocity; @@ -77,7 +79,8 @@ typedef enum { /* 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, - DEPTH_BOTTOM, TEMP_TEXT, TEMP_PLOT, SAC_DEFAULT, BOUNDING_BOX, PRESSURE_TEXT, BACKGROUND + DEPTH_BOTTOM, TEMP_TEXT, TEMP_PLOT, SAC_DEFAULT, BOUNDING_BOX, PRESSURE_TEXT, BACKGROUND, + CEILING_SHALLOW, CEILING_DEEP } color_indice_t; typedef struct { @@ -129,6 +132,9 @@ static const color_t profile_color[] = { [BOUNDING_BOX] = {{WHITE1, BLACK1_LOW_TRANS}}, [PRESSURE_TEXT] = {{KILLARNEY1, BLACK1_LOW_TRANS}}, [BACKGROUND] = {{SPRINGWOOD1, BLACK1_LOW_TRANS}}, + [CEILING_SHALLOW] = {{REDORANGE1_HIGH_TRANS, REDORANGE1_HIGH_TRANS}}, + [CEILING_DEEP] = {{RED1_MED_TRANS, RED1_MED_TRANS}}, + }; #define plot_info_size(nr) (sizeof(struct plot_info) + (nr)*sizeof(struct plot_data)) @@ -185,11 +191,11 @@ 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 po2:%lf phe:%lf pn2:%lf sum-pp %lf}\n", + " time:%d:%02d temperature:%d depth:%d ceiling:%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].ceiling, 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"); @@ -294,6 +300,7 @@ static void plot_text(struct graphics_context *gc, const text_render_options_t * cairo_show_text(cr, buffer); } +/* collect all event names and whether we display them */ struct ev_select { char *ev_name; gboolean plot_ev; @@ -314,12 +321,12 @@ void evn_foreach(void (*callback)(const char *, int *, void *), void *data) void remember_event(const char *eventname) { - int i=0, len; + int i = 0, len; if (!eventname || (len = strlen(eventname)) == 0) return; while (i < evn_used) { - if (!strncmp(eventname,ev_namelist[i].ev_name,len)) + if (!strncmp(eventname, ev_namelist[i].ev_name, len)) return; i++; } @@ -339,6 +346,7 @@ static void plot_one_event(struct graphics_context *gc, struct plot_info *pi, st { int i, depth = 0; int x,y; + char buffer[80]; /* is plotting this event disabled? */ if (event->name) { @@ -374,7 +382,11 @@ static void plot_one_event(struct graphics_context *gc, struct plot_info *pi, st cairo_line_to(gc->cr, x-9, y+4); cairo_stroke(gc->cr); /* we display the event on screen - so translate */ - attach_tooltip(x-15, y-6, 12, 12, _(event->name)); + if (event->value) + snprintf(buffer, sizeof(buffer), "%s: %d", _(event->name), event->value); + else + snprintf(buffer, sizeof(buffer), "%s", _(event->name)); + attach_tooltip(x-15, y-6, 12, 12, buffer); } static void plot_events(struct graphics_context *gc, struct plot_info *pi, struct dive *dive) @@ -386,7 +398,8 @@ static void plot_events(struct graphics_context *gc, struct plot_info *pi, struc return; while (event) { - plot_one_event(gc, pi, event, &tro); + if (event->flags != SAMPLE_FLAGS_BEGIN && event->flags != SAMPLE_FLAGS_END) + plot_one_event(gc, pi, event, &tro); event = event->next; } } @@ -928,8 +941,37 @@ static void plot_depth_profile(struct graphics_context *gc, struct plot_info *pi move_to(gc, 0, 0); for (i = 0; i < pi->nr; i++, entry++) line_to(gc, entry->sec, entry->depth); + + /* Show any ceiling we may have encountered */ + for (i = pi->nr - 1; i >= 0; i--, entry--) { + if (entry->ceiling < entry->depth) { + line_to(gc, entry->sec, entry->ceiling); + } else { + line_to(gc, entry->sec, entry->depth); + } + } cairo_close_path(gc->cr); + cairo_fill(gc->cr); + + /* next show where we have been bad and crossed the ceiling */ + pat = cairo_pattern_create_linear (0.0, 0.0, 0.0, 256.0 * plot_scale); + pattern_add_color_stop_rgba (gc, pat, 0, CEILING_SHALLOW); + pattern_add_color_stop_rgba (gc, pat, 1, CEILING_DEEP); + cairo_set_source(gc->cr, pat); + cairo_pattern_destroy(pat); + entry = pi->entry; + move_to(gc, 0, 0); + for (i = 0; i < pi->nr; i++, entry++) + line_to(gc, entry->sec, entry->depth); + for (i = pi->nr - 1; i >= 0; i--, entry--) { + if (entry->ceiling > entry->depth) { + line_to(gc, entry->sec, entry->ceiling); + } else { + line_to(gc, entry->sec, entry->depth); + } + } + cairo_close_path(gc->cr); cairo_fill(gc->cr); /* Now do it again for the velocity colors */ @@ -1509,10 +1551,12 @@ static int get_cylinder_index(struct dive *dive, struct event *ev) return 0; } -static struct event *get_next_gaschange(struct event *event) +static struct event *get_next_event(struct event *event, char *name) { + if (!name || !*name) + return NULL; while (event) { - if (!strcmp(event->name, "gaschange")) + if (!strcmp(event->name, name)) return event; event = event->next; } @@ -1537,7 +1581,7 @@ static int set_cylinder_index(struct plot_info *pi, int i, int cylinderindex, un static void check_gas_change_events(struct dive *dive, struct plot_info *pi) { int i = 0, cylinderindex = 0; - struct event *ev = get_next_gaschange(dive->events); + struct event *ev = get_next_event(dive->events, "gaschange"); if (!ev) return; @@ -1545,7 +1589,7 @@ static void check_gas_change_events(struct dive *dive, struct plot_info *pi) do { i = set_cylinder_index(pi, i, cylinderindex, ev->time.seconds); cylinderindex = get_cylinder_index(dive, ev); - ev = get_next_gaschange(ev->next); + ev = get_next_event(ev->next, "gaschange"); } while (ev); set_cylinder_index(pi, i, cylinderindex, ~0u); } @@ -1554,11 +1598,11 @@ static void check_gas_change_events(struct dive *dive, struct plot_info *pi) static int count_gas_change_events(struct dive *dive) { int count = 0; - struct event *ev = get_next_gaschange(dive->events); + struct event *ev = get_next_event(dive->events, "gaschange"); while (ev) { count++; - ev = get_next_gaschange(ev->next); + ev = get_next_event(ev->next, "gaschange"); } return count; } @@ -1574,14 +1618,14 @@ static struct plot_info *create_plot_info(struct dive *dive, int nr_samples, str { int cylinderindex = -1; int lastdepth, lastindex; - int i, pi_idx, nr, sec, cyl; + int i, pi_idx, nr, sec, cyl, ceiling = 0; size_t alloc_size; struct plot_info *pi; pr_track_t *track_pr[MAX_CYLINDERS] = {NULL, }; pr_track_t *pr_track, *current; gboolean missing_pr = FALSE; struct plot_data *entry = NULL; - struct event *ev; + struct event *ev, *ceil_ev; /* we want to potentially add synthetic plot_info elements for the gas changes */ nr = nr_samples + 4 + 2 * count_gas_change_events(dive); @@ -1593,18 +1637,20 @@ static struct plot_info *create_plot_info(struct dive *dive, int nr_samples, str pi->nr = nr; pi_idx = 2; /* the two extra events at the start */ /* check for gas changes before the samples start */ - ev = get_next_gaschange(dive->events); + ev = get_next_event(dive->events, "gaschange"); while (ev && ev->time.seconds < dive_sample->time.seconds) { entry = pi->entry + pi_idx; entry->sec = ev->time.seconds; entry->depth = 0; /* is that always correct ? */ pi_idx++; - ev = get_next_gaschange(ev->next); + ev = get_next_event(ev->next, "gaschange"); } if (ev && ev->time.seconds == dive_sample->time.seconds) { /* we already have a sample at the time of the event */ - ev = get_next_gaschange(ev->next); + ev = get_next_event(ev->next, "gaschange"); } + /* find the first deco/ceiling event (if any) */ + ceil_ev = get_next_event(dive->events, "ceiling"); sec = 0; lastindex = 0; lastdepth = -1; @@ -1614,13 +1660,22 @@ static struct plot_info *create_plot_info(struct dive *dive, int nr_samples, str struct sample *sample = dive_sample+i; entry = pi->entry + i + pi_idx; + while (ceil_ev && ceil_ev->time.seconds <= sample->time.seconds) { + struct event *next_ceil_ev = get_next_event(ceil_ev->next, "ceiling"); + if (!next_ceil_ev || next_ceil_ev->time.seconds > sample->time.seconds) + break; + ceil_ev = next_ceil_ev; + } + if (ceil_ev && ceil_ev->time.seconds <= sample->time.seconds) { + ceiling = ceil_ev->value; + ceil_ev = get_next_event(ceil_ev->next, "ceiling"); + } while (ev && ev->time.seconds < sample->time.seconds) { /* insert two fake plot info structures for the end of * the old tank and the start of the new tank */ if (ev->time.seconds == sample->time.seconds - 1) { entry->sec = ev->time.seconds - 1; (entry+1)->sec = ev->time.seconds; - } else { entry->sec = ev->time.seconds; (entry+1)->sec = ev->time.seconds + 1; } @@ -1630,10 +1685,12 @@ static struct plot_info *create_plot_info(struct dive *dive, int nr_samples, str (sample->depth.mm - (sample-1)->depth.mm) / 2; } else entry->depth = sample->depth.mm; - (entry+1)->depth = entry->depth; + (entry + 1)->depth = entry->depth; + entry->ceiling = ceiling; + (entry + 1)->ceiling = ceiling; pi_idx += 2; entry = pi->entry + i + pi_idx; - ev = get_next_gaschange(ev->next); + ev = get_next_event(ev->next, "gaschange"); } if (ev && ev->time.seconds == sample->time.seconds) { /* we already have a sample at the time of the event @@ -1641,13 +1698,15 @@ static struct plot_info *create_plot_info(struct dive *dive, int nr_samples, str * real even by one second (to keep time monotonous) */ entry->sec = ev->time.seconds; entry->depth = sample->depth.mm; + entry->ceiling = ceiling; pi_idx++; entry = pi->entry + i + pi_idx; - ev = get_next_gaschange(ev->next); + ev = get_next_event(ev->next, "gaschange"); delay = 1; } sec = entry->sec = sample->time.seconds + delay; depth = entry->depth = sample->depth.mm; + entry->ceiling = ceiling; entry->cylinderindex = sample->cylinderindex; SENSOR_PRESSURE(entry) = sample->cylinderpressure.mbar; entry->temperature = sample->temperature.mkelvin; @@ -1666,7 +1725,7 @@ static struct plot_info *create_plot_info(struct dive *dive, int nr_samples, str entry->depth = 0; /* why are there gas changes after the dive is over? */ pi_idx++; entry = pi->entry + i + pi_idx; - ev = get_next_gaschange(ev->next); + ev = get_next_event(ev->next, "gaschange"); } nr = nr_samples + pi_idx - 2; check_gas_change_events(dive, pi); diff --git a/uemis-downloader.c b/uemis-downloader.c index 5267a0b00..6f85eca59 100644 --- a/uemis-downloader.c +++ b/uemis-downloader.c @@ -792,7 +792,15 @@ static char *do_uemis_download(struct argument_block *args) if (! uemis_get_answer(mountpath, "processSync", 0, 2, &result)) goto bail; param_buff[1] = "notempty"; - newmax = get_divenr(*max_dive_data, deviceid); + /* if we have an empty divelist then the user will almost + * certainly want to start downloading from the first dive on + * the Uemis; otherwise check which was the last dive + * downloaded */ + if (dive_table.nr > 0) + newmax = get_divenr(*max_dive_data, deviceid); + else + newmax = strdup("0"); + if (sscanf(newmax, "%d", &start) != 1) start = 0; for (;;) { @@ -18,6 +18,7 @@ #include "dive.h" #include "uemis.h" +#include <libdivecomputer/parser.h> /* * following code is based on code found in at base64.sourceforge.net/b64.c @@ -119,7 +120,9 @@ bail: return datalen; } -/* Create events from the flag bits; +static gboolean in_deco; + +/* Create events from the flag bits and other data in the sample; * These bits basically represent what is displayed on screen at sample time. * Many of these 'warnings' are way hyper-active and seriously clutter the * profile plot - so these are disabled by default @@ -175,6 +178,21 @@ void uemis_event(struct dive *dive, struct sample *sample, uemis_sample_t *u_sam add_event(dive, sample->time.seconds, 0, 0, 0, N_("Low Battery Alert")); /* flags[7] reflects the little on screen icons that remind of previous * warnings / alerts - not useful for events */ + + /* now add deco / ceiling events */ + if (u_sample->p_amb_tol > dive->surface_pressure.mbar && + u_sample->hold_time && + u_sample->hold_time < 99) { + add_event(dive, sample->time.seconds, SAMPLE_EVENT_CEILING, SAMPLE_FLAGS_BEGIN, + u_sample->hold_depth * 10, N_("ceiling")); + add_event(dive, sample->time.seconds, SAMPLE_EVENT_DECOSTOP, 0, + u_sample->hold_time * 60, N_("deco")); + in_deco = TRUE; + } else if (in_deco) { + in_deco = FALSE; + add_event(dive, sample->time.seconds, SAMPLE_EVENT_CEILING, SAMPLE_FLAGS_END, + 0, N_("ceiling")); + } } /* @@ -190,10 +208,11 @@ void uemis_parse_divelog_binary(char *base64, void *datap) { struct dive *dive = *divep; int template, gasoffset; + in_deco = FALSE; datalen = uemis_convert_base64(base64, &data); dive->airtemp.mkelvin = *(uint16_t *)(data + 45) * 100 + 273150; - + dive->surface_pressure.mbar = *(uint16_t *)(data +43); /* dive template in use: 0 = air 1 = nitrox (B) |