X-Git-Url: http://git.tdb.fi/?a=blobdiff_plain;f=profile.c;h=ad158fe8a1f9aaafb40b792f31dea13667594f5f;hb=edbba678b3cdc8f06a84a965b04f13ef1f4ecaab;hp=2edaf694cd5f429387d5e3cba08f32c7fd3aeda8;hpb=ab3c6731bea1043e35d450b227fc2f41e137c397;p=ext%2Fsubsurface.git diff --git a/profile.c b/profile.c index 2edaf69..ad158fe 100644 --- a/profile.c +++ b/profile.c @@ -22,12 +22,18 @@ struct plot_info { int maxtime; int meandepth, maxdepth; int minpressure, maxpressure; + int endpressure; /* start pressure better be max pressure */ int mintemp, maxtemp; struct plot_data { + unsigned int same_cylinder:1; + unsigned int cylinderindex; int sec; - int pressure, temperature; + /* pressure[0] is sensor pressure + * pressure[1] is interpolated pressure */ + int pressure[2]; + int temperature; /* Depth info */ - int val; + int depth; int smoothed; velocity_t velocity; struct plot_data *min[3]; @@ -35,6 +41,10 @@ struct plot_info { int avg[3]; } entry[]; }; +#define SENSOR_PR 0 +#define INTERPOLATED_PR 1 +#define SENSOR_PRESSURE(_entry) (_entry)->pressure[SENSOR_PR] +#define INTERPOLATED_PRESSURE(_entry) (_entry)->pressure[INTERPOLATED_PR] /* convert velocity to colors */ typedef struct { double r, g, b; } rgb_t; @@ -156,17 +166,85 @@ static void plot_text(struct graphics_context *gc, const text_render_options_t * cairo_show_text(cr, buffer); } +struct ev_select { + char *ev_name; + gboolean plot_ev; +}; +static struct ev_select *ev_namelist; +static int evn_allocated; +static int evn_used; + +void evn_foreach(void (*callback)(const char *, int *, void *), void *data) +{ + int i; + + for (i = 0; i < evn_used; i++) { + callback(ev_namelist[i].ev_name, &ev_namelist[i].plot_ev, data); + } +} + +void remember_event(const char *eventname) +{ + int i=0, len; + + if (!eventname || (len = strlen(eventname)) == 0) + return; + while (i < evn_used) { + if (!strncmp(eventname,ev_namelist[i].ev_name,len)) + return; + i++; + } + if (evn_used == evn_allocated) { + evn_allocated += 10; + ev_namelist = realloc(ev_namelist, evn_allocated * sizeof(struct ev_select)); + if (! ev_namelist) + /* we are screwed, but let's just bail out */ + return; + } + ev_namelist[evn_used].ev_name = strdup(eventname); + ev_namelist[evn_used].plot_ev = TRUE; + evn_used++; +} + static void plot_one_event(struct graphics_context *gc, struct plot_info *pi, struct event *event, const text_render_options_t *tro) { int i, depth = 0; - + int x,y; + + /* is plotting this event disabled? */ + if (event->name) { + for (i = 0; i < evn_used; i++) { + if (! strcmp(event->name, ev_namelist[i].ev_name)) { + if (ev_namelist[i].plot_ev) + break; + else + return; + } + } + } for (i = 0; i < pi->nr; i++) { struct plot_data *data = pi->entry + i; if (event->time.seconds < data->sec) break; - depth = data->val; + depth = data->depth; } - plot_text(gc, tro, event->time.seconds, depth, "%s", event->name); + /* draw a little tirangular marker and attach tooltip */ + x = SCALEX(gc, event->time.seconds); + y = SCALEY(gc, depth); + set_source_rgba(gc, 1.0, 1.0, 0.1, 0.8); + cairo_move_to(gc->cr, x-15, y+6); + cairo_line_to(gc->cr, x-3 , y+6); + cairo_line_to(gc->cr, x-9, y-6); + cairo_line_to(gc->cr, x-15, y+6); + cairo_stroke_preserve(gc->cr); + cairo_fill(gc->cr); + set_source_rgba(gc, 0.0, 0.0, 0.0, 0.8); + cairo_move_to(gc->cr, x-9, y-3); + cairo_line_to(gc->cr, x-9, y+1); + cairo_move_to(gc->cr, x-9, y+4); + cairo_line_to(gc->cr, x-9, y+4); + cairo_stroke(gc->cr); + attach_tooltip(x-15, y-6, 12, 12, event->name); } static void plot_events(struct graphics_context *gc, struct plot_info *pi, struct dive *dive) @@ -188,9 +266,9 @@ static void render_depth_sample(struct graphics_context *gc, struct plot_data *e int sec = entry->sec, decimals; double d; - d = get_depth_units(entry->val, &decimals, NULL); + d = get_depth_units(entry->depth, &decimals, NULL); - plot_text(gc, tro, sec, entry->val, "%.*f", decimals, d); + plot_text(gc, tro, sec, entry->depth, "%.*f", decimals, d); } static void plot_text_samples(struct graphics_context *gc, struct plot_info *pi) @@ -198,18 +276,26 @@ static void plot_text_samples(struct graphics_context *gc, struct plot_info *pi) static const text_render_options_t deep = {14, 1.0, 0.2, 0.2, CENTER, TOP}; static const text_render_options_t shallow = {14, 1.0, 0.2, 0.2, CENTER, BOTTOM}; int i; + int last = -1; for (i = 0; i < pi->nr; i++) { struct plot_data *entry = pi->entry + i; - if (entry->val < 2000) + if (entry->depth < 2000) continue; - if (entry == entry->max[2]) + if ((entry == entry->max[2]) && entry->depth != last) { render_depth_sample(gc, entry, &deep); + last = entry->depth; + } - if (entry == entry->min[2]) + if ((entry == entry->min[2]) && entry->depth != last) { render_depth_sample(gc, entry, &shallow); + last = entry->depth; + } + + if (entry->depth != last) + last = -1; } } @@ -248,13 +334,13 @@ static void plot_minmax_profile_minute(struct graphics_context *gc, struct plot_ struct plot_data *entry = pi->entry; set_source_rgba(gc, 1, 0.2, 1, a); - move_to(gc, entry->sec, entry->min[index]->val); + move_to(gc, entry->sec, entry->min[index]->depth); for (i = 1; i < pi->nr; i++) { entry++; - line_to(gc, entry->sec, entry->min[index]->val); + line_to(gc, entry->sec, entry->min[index]->depth); } for (i = 1; i < pi->nr; i++) { - line_to(gc, entry->sec, entry->max[index]->val); + line_to(gc, entry->sec, entry->max[index]->depth); entry--; } cairo_close_path(gc->cr); @@ -272,23 +358,43 @@ static void plot_minmax_profile(struct graphics_context *gc, struct plot_info *p static void plot_depth_profile(struct graphics_context *gc, struct plot_info *pi) { - int i; + int i, incr; cairo_t *cr = gc->cr; int sec, depth; struct plot_data *entry; int maxtime, maxdepth, marker; + int increments[4] = { 5*60, 10*60, 15*60, 30*60 }; /* Get plot scaling limits */ maxtime = get_maxtime(pi); maxdepth = get_maxdepth(pi); - /* Time markers: every 5 min */ + /* Time markers: at most every 5 min, but no more than 12 markers + * and for convenience we do 5, 10, 15 or 30 min intervals. + * This allows for 6h dives - enough (I hope) for even the craziest + * divers - but just in case, for those 8h depth-record-breaking dives, + * we double the interval if this still doesn't get us to 12 or fewer + * time markers */ + i = 0; + while (maxtime / increments[i] > 12 && i < 4) + i++; + incr = increments[i]; + while (maxtime / incr > 12) + incr *= 2; + gc->leftx = 0; gc->rightx = maxtime; gc->topy = 0; gc->bottomy = 1.0; - for (i = 5*60; i < maxtime; i += 5*60) { + set_source_rgba(gc, 1, 1, 1, 0.5); + for (i = incr; i < maxtime; i += incr) { move_to(gc, i, 0); line_to(gc, i, 1); } + cairo_stroke(cr); + + /* now the text on every second time marker */ + text_render_options_t tro = {10, 0.2, 1.0, 0.2, CENTER, TOP}; + for (i = incr; i < maxtime; i += 2 * incr) + plot_text(gc, &tro, i, 1, "%d", i/60); /* Depth markers: every 30 ft or 10 m*/ gc->leftx = 0; gc->rightx = 1.0; @@ -333,7 +439,7 @@ static void plot_depth_profile(struct graphics_context *gc, struct plot_info *pi entry = pi->entry; move_to(gc, 0, 0); for (i = 0; i < pi->nr; i++, entry++) - line_to(gc, entry->sec, entry->val); + line_to(gc, entry->sec, entry->depth); cairo_close_path(gc->cr); if (gc->printer) { set_source_rgba(gc, 1, 1, 1, 0.2); @@ -353,9 +459,9 @@ static void plot_depth_profile(struct graphics_context *gc, struct plot_info *pi * representing the vertical velocity, so we need to * chop this into short segments */ rgb_t color = rgb[entry->velocity]; - depth = entry->val; + depth = entry->depth; set_source_rgb(gc, color.r, color.g, color.b); - move_to(gc, entry[-1].sec, entry[-1].val); + move_to(gc, entry[-1].sec, entry[-1].depth); line_to(gc, sec, depth); cairo_stroke(cr); } @@ -387,19 +493,13 @@ static int setup_temperature_limits(struct graphics_context *gc, struct plot_inf static void plot_single_temp_text(struct graphics_context *gc, int sec, int mkelvin) { - int deg; + double deg; const char *unit; static const text_render_options_t tro = {12, 0.2, 0.2, 1.0, LEFT, TOP}; - temperature_t temperature = { mkelvin }; - if (output_units.temperature == FAHRENHEIT) { - deg = to_F(temperature); - unit = UTF8_DEGREE "F"; - } else { - deg = to_C(temperature); - unit = UTF8_DEGREE "C"; - } - plot_text(gc, &tro, sec, temperature.mkelvin, "%d%s", deg, unit); + deg = get_temp_units(mkelvin, &unit); + + plot_text(gc, &tro, sec, mkelvin, "%d%s", (int)(deg + 0.5), unit); } static void plot_temperature_text(struct graphics_context *gc, struct plot_info *pi) @@ -468,63 +568,109 @@ static int get_cylinder_pressure_range(struct graphics_context *gc, struct plot_ return pi->maxpressure != 0; } -static void plot_cylinder_pressure(struct graphics_context *gc, struct plot_info *pi) +static void plot_pressure_helper(struct graphics_context *gc, struct plot_info *pi, int type) { int i; + int lift_pen = FALSE; - if (!get_cylinder_pressure_range(gc, pi)) - return; - - set_source_rgba(gc, 0.2, 1.0, 0.2, 0.80); - - move_to(gc, 0, pi->maxpressure); - for (i = 1; i < pi->nr; i++) { + for (i = 0; i < pi->nr; i++) { int mbar; struct plot_data *entry = pi->entry + i; - mbar = entry->pressure; - if (!mbar) + mbar = entry->pressure[type]; + if (!entry->same_cylinder) + lift_pen = TRUE; + if (!mbar) { + lift_pen = TRUE; continue; - line_to(gc, entry->sec, mbar); + } + if (lift_pen) { + if (i > 0 && entry->same_cylinder) { + /* if we have a previous event from the same tank, + * draw at least a short line . + * This uses the implementation detail that the + * type is either 0 or 1 */ + int prev_pr; + prev_pr = (entry-1)->pressure[type] ? : (entry-1)->pressure[1 - type]; + move_to(gc, (entry-1)->sec, prev_pr); + line_to(gc, entry->sec, mbar); + } else + move_to(gc, entry->sec, mbar); + lift_pen = FALSE; + } + else + line_to(gc, entry->sec, mbar); } - line_to(gc, pi->maxtime, pi->minpressure); cairo_stroke(gc->cr); + +} + +static void plot_cylinder_pressure(struct graphics_context *gc, struct plot_info *pi) +{ + if (!get_cylinder_pressure_range(gc, pi)) + return; + + /* first plot the pressure readings we have from the dive computer */ + set_source_rgba(gc, 0.2, 1.0, 0.2, 0.80); + plot_pressure_helper(gc, pi, SENSOR_PR); + + /* then, in a different color, the interpolated values */ + set_source_rgba(gc, 1.0, 1.0, 0.2, 0.80); + plot_pressure_helper(gc, pi, INTERPOLATED_PR); } -static int mbar_to_PSI(int mbar) +static void plot_pressure_value(struct graphics_context *gc, int mbar, int sec, + int xalign, int yalign) { - pressure_t p = {mbar}; - return to_PSI(p); + int pressure; + const char *unit; + + pressure = get_pressure_units(mbar, &unit); + text_render_options_t tro = {10, 0.2, 1.0, 0.2, xalign, yalign}; + plot_text(gc, &tro, sec, mbar, "%d %s", pressure, unit); } static void plot_cylinder_pressure_text(struct graphics_context *gc, struct plot_info *pi) { - if (get_cylinder_pressure_range(gc, pi)) { - int start, end; - const char *unit = "bar"; + int i; + int mbar, cyl; + int seen_cyl[MAX_CYLINDERS] = { FALSE, }; + int last_pressure[MAX_CYLINDERS] = { 0, }; + int last_time[MAX_CYLINDERS] = { 0, }; + struct plot_data *entry; - switch (output_units.pressure) { - case PASCAL: - start = pi->maxpressure * 100; - end = pi->minpressure * 100; - unit = "pascal"; - break; - case BAR: - start = (pi->maxpressure + 500) / 1000; - end = (pi->minpressure + 500) / 1000; - unit = "bar"; - break; - case PSI: - start = mbar_to_PSI(pi->maxpressure); - end = mbar_to_PSI(pi->minpressure); - unit = "psi"; - break; + if (!get_cylinder_pressure_range(gc, pi)) + return; + + /* only loop over the actual events from the dive computer */ + for (i = 2; i < pi->nr; i++) { + entry = pi->entry + i; + + if (!entry->same_cylinder) { + cyl = entry->cylinderindex; + if (!seen_cyl[cyl]) { + mbar = SENSOR_PRESSURE(entry) ? : INTERPOLATED_PRESSURE(entry); + plot_pressure_value(gc, mbar, entry->sec, LEFT, BOTTOM); + seen_cyl[cyl] = TRUE; + } + if (i > 2) { + /* remember the last pressure and time of + * the previous cylinder */ + cyl = (entry - 1)->cylinderindex; + last_pressure[cyl] = + SENSOR_PRESSURE(entry - 1) ? : INTERPOLATED_PRESSURE(entry - 1); + last_time[cyl] = (entry - 1)->sec; + } } + } + cyl = entry->cylinderindex; + last_pressure[cyl] = SENSOR_PRESSURE(entry) ? : INTERPOLATED_PRESSURE(entry); + last_time[cyl] = entry->sec; - text_render_options_t tro = {10, 0.2, 1.0, 0.2, LEFT, TOP}; - plot_text(gc, &tro, 0, pi->maxpressure, "%d %s", start, unit); - plot_text(gc, &tro, pi->maxtime, pi->minpressure, - "%d %s", end, unit); + for (cyl = 0; cyl < MAX_CYLINDERS; cyl++) { + if (last_time[cyl]) { + plot_pressure_value(gc, last_pressure[cyl], last_time[cyl], CENTER, TOP); + } } } @@ -545,17 +691,17 @@ static void analyze_plot_info_minmax_minute(struct plot_data *entry, struct plot /* Then go forward until we hit an entry past the time */ min = max = p; - avg = p->val; + avg = p->depth; nr = 1; while (++p < last) { - int val = p->val; + int depth = p->depth; if (p->sec > time + seconds) break; - avg += val; + avg += depth; nr ++; - if (val < min->val) + if (depth < min->depth) min = p; - if (val > max->val) + if (depth > max->depth) max = p; } entry->min[index] = min; @@ -605,7 +751,7 @@ static struct plot_info *analyze_plot_info(struct plot_info *pi) /* Do pressure min/max based on the non-surface data */ for (i = 0; i < nr; i++) { struct plot_data *entry = pi->entry+i; - int pressure = entry->pressure; + int pressure = SENSOR_PRESSURE(entry) ? : INTERPOLATED_PRESSURE(entry); int temperature = entry->temperature; if (pressure) { @@ -626,22 +772,22 @@ static struct plot_info *analyze_plot_info(struct plot_info *pi) /* Smoothing function: 5-point triangular smooth */ for (i = 2; i < nr; i++) { struct plot_data *entry = pi->entry+i; - int val; + int depth; if (i < nr-2) { - val = entry[-2].val + 2*entry[-1].val + 3*entry[0].val + 2*entry[1].val + entry[2].val; - entry->smoothed = (val+4) / 9; + depth = entry[-2].depth + 2*entry[-1].depth + 3*entry[0].depth + 2*entry[1].depth + entry[2].depth; + entry->smoothed = (depth+4) / 9; } /* vertical velocity in mm/sec */ /* Linus wants to smooth this - let's at least look at the samples that aren't FAST or CRAZY */ if (entry[0].sec - entry[-1].sec) { - entry->velocity = velocity((entry[0].val - entry[-1].val) / (entry[0].sec - entry[-1].sec)); + entry->velocity = velocity((entry[0].depth - entry[-1].depth) / (entry[0].sec - entry[-1].sec)); /* if our samples are short and we aren't too FAST*/ if (entry[0].sec - entry[-1].sec < 15 && entry->velocity < FAST) { int past = -2; while (i+past > 0 && entry[0].sec - entry[past].sec < 15) past--; - entry->velocity = velocity((entry[0].val - entry[past].val) / + entry->velocity = velocity((entry[0].depth - entry[past].depth) / (entry[0].sec - entry[past].sec)); } } else @@ -657,6 +803,181 @@ static struct plot_info *analyze_plot_info(struct plot_info *pi) return pi; } +/* + * simple structure to track the beginning and end tank pressure as + * well as the integral of depth over time spent while we have no + * pressure reading from the tank */ +typedef struct pr_track_struct pr_track_t; +struct pr_track_struct { + int start; + int end; + int t_start; + int t_end; + double pressure_time; + pr_track_t *next; +}; + +static pr_track_t *pr_track_alloc(int start, int t_start) { + pr_track_t *pt = malloc(sizeof(pr_track_t)); + pt->start = start; + pt->t_start = t_start; + pt->end = 0; + pt->t_end = 0; + pt->pressure_time = 0.0; + pt->next = NULL; + return pt; +} + +/* poor man's linked list */ +static pr_track_t *list_last(pr_track_t *list) +{ + pr_track_t *tail = list; + if (!tail) + return NULL; + while (tail->next) { + tail = tail->next; + } + return tail; +} + +static pr_track_t *list_add(pr_track_t *list, pr_track_t *element) +{ + pr_track_t *tail = list_last(list); + if (!tail) + return element; + tail->next = element; + return list; +} + +static void list_free(pr_track_t *list) +{ + if (!list) + return; + list_free(list->next); + free(list); +} + +static void fill_missing_tank_pressures(struct dive *dive, struct plot_info *pi, + pr_track_t **track_pr) +{ + pr_track_t *list = NULL; + pr_track_t *nlist = NULL; + double pt, magic; + int cyl, i; + struct plot_data *entry; + int cur_pr[MAX_CYLINDERS]; + + for (cyl = 0; cyl < MAX_CYLINDERS; cyl++) { + cur_pr[cyl] = track_pr[cyl]->start; + } + + /* The first two are "fillers" */ + for (i = 2; i < pi->nr; i++) { + entry = pi->entry + i; + if (SENSOR_PRESSURE(entry)) { + cur_pr[entry->cylinderindex] = SENSOR_PRESSURE(entry); + } else { + if(!list || list->t_end < entry->sec) { + nlist = track_pr[entry->cylinderindex]; + list = NULL; + while (nlist && nlist->t_start <= entry->sec) { + list = nlist; + nlist = list->next; + } + /* there may be multiple segments - so + * let's assemble the length */ + nlist = list; + pt = list->pressure_time; + while (!nlist->end) { + nlist = nlist->next; + if (!nlist) { + /* oops - we have no end pressure, + * so this means this is a tank without + * gas consumption information */ + break; + } + pt += nlist->pressure_time; + } + if (!nlist) { + /* just continue without calculating + * interpolated values */ + list = NULL; + continue; + } + magic = (nlist->end - cur_pr[entry->cylinderindex]) / pt; } + if (pt != 0.0) { + double cur_pt = (entry->sec - (entry-1)->sec) * + (1 + entry->depth / 10000.0); + INTERPOLATED_PRESSURE(entry) = + cur_pr[entry->cylinderindex] + cur_pt * magic; + cur_pr[entry->cylinderindex] = INTERPOLATED_PRESSURE(entry); + } + } + } +} + +static int get_cylinder_index(struct dive *dive, struct event *ev) +{ + int i; + + /* + * Try to find a cylinder that matches the O2 percentage + * in the gas change event 'value' field. + * + * Crazy suunto gas change events. We really should do + * this in libdivecomputer or something. + */ + for (i = 0; i < MAX_CYLINDERS; i++) { + cylinder_t *cyl = dive->cylinder+i; + int o2 = (cyl->gasmix.o2.permille + 5) / 10; + if (o2 == ev->value) + return i; + } + + return 0; +} + +static struct event *get_next_gaschange(struct event *event) +{ + while (event) { + if (!strcmp(event->name, "gaschange")) + return event; + event = event->next; + } + return event; +} + +static int set_cylinder_index(struct plot_info *pi, int i, int cylinderindex, unsigned int end) +{ + while (i < pi->nr) { + struct plot_data *entry = pi->entry+i; + if (entry->sec > end) + break; + if (entry->cylinderindex != cylinderindex) { + entry->cylinderindex = cylinderindex; + entry->pressure[0] = 0; + } + i++; + } + return i; +} + +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); + + if (!ev) + return; + + do { + i = set_cylinder_index(pi, i, cylinderindex, ev->time.seconds); + cylinderindex = get_cylinder_index(dive, ev); + ev = get_next_gaschange(ev->next); + } while (ev); + set_cylinder_index(pi, i, cylinderindex, ~0u); +} + /* * Create a plot-info with smoothing and ranged min/max * @@ -664,12 +985,17 @@ static struct plot_info *analyze_plot_info(struct plot_info *pi) * sides, so that you can do end-points without having to worry * about it. */ -static struct plot_info *create_plot_info(struct dive *dive) +static struct plot_info *create_plot_info(struct dive *dive, int nr_samples, struct sample *dive_sample) { + int cylinderindex = -1; int lastdepth, lastindex; - int i, nr = dive->samples + 4, sec; + int i, nr = nr_samples + 4, sec, cyl; size_t alloc_size = plot_info_size(nr); 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; pi = malloc(alloc_size); if (!pi) @@ -679,14 +1005,15 @@ static struct plot_info *create_plot_info(struct dive *dive) sec = 0; lastindex = 0; lastdepth = -1; - for (i = 0; i < dive->samples; i++) { + for (i = 0; i < nr_samples; i++) { int depth; - struct sample *sample = dive->sample+i; - struct plot_data *entry = pi->entry + i + 2; + struct sample *sample = dive_sample+i; + entry = pi->entry + i + 2; sec = entry->sec = sample->time.seconds; - depth = entry->val = sample->depth.mm; - entry->pressure = sample->cylinderpressure.mbar; + depth = entry->depth = sample->depth.mm; + entry->cylinderindex = sample->cylinderindex; + SENSOR_PRESSURE(entry) = sample->cylinderpressure.mbar; entry->temperature = sample->temperature.mkelvin; if (depth || lastdepth) @@ -696,32 +1023,93 @@ static struct plot_info *create_plot_info(struct dive *dive) if (depth > pi->maxdepth) pi->maxdepth = depth; } - if (lastdepth) - lastindex = i + 2; + + check_gas_change_events(dive, pi); + + for (cyl = 0; cyl < MAX_CYLINDERS; cyl++) /* initialize the start pressures */ + track_pr[cyl] = pr_track_alloc(dive->cylinder[cyl].start.mbar, -1); + current = track_pr[pi->entry[2].cylinderindex]; + for (i = 0; i < nr_samples; i++) { + entry = pi->entry + i + 2; + + entry->same_cylinder = entry->cylinderindex == cylinderindex; + cylinderindex = entry->cylinderindex; + + /* track the segments per cylinder and their pressure/time integral */ + if (!entry->same_cylinder) { + current->end = SENSOR_PRESSURE(entry-1); + current->t_end = (entry-1)->sec; + current = pr_track_alloc(SENSOR_PRESSURE(entry), entry->sec); + track_pr[cylinderindex] = list_add(track_pr[cylinderindex], current); + } else { /* same cylinder */ + if ((!SENSOR_PRESSURE(entry) && SENSOR_PRESSURE(entry-1)) || + (SENSOR_PRESSURE(entry) && !SENSOR_PRESSURE(entry-1))) { + /* transmitter changed its working status */ + current->end = SENSOR_PRESSURE(entry-1); + current->t_end = (entry-1)->sec; + current = pr_track_alloc(SENSOR_PRESSURE(entry), entry->sec); + track_pr[cylinderindex] = + list_add(track_pr[cylinderindex], current); + } + } + /* finally, do the discrete integration to get the SAC rate equivalent */ + current->pressure_time += (entry->sec - (entry-1)->sec) * + (1 + entry->depth / 10000.0); + missing_pr |= !SENSOR_PRESSURE(entry); + } + + if (entry) + current->t_end = entry->sec; + + for (cyl = 0; cyl < MAX_CYLINDERS; cyl++) { /* initialize the end pressures */ + int pr = dive->cylinder[cyl].end.mbar; + if (pr && track_pr[cyl]) { + pr_track = list_last(track_pr[cyl]); + pr_track->end = pr; + } + } /* Fill in the last two entries with empty values but valid times */ - i = dive->samples + 2; + i = nr_samples + 2; pi->entry[i].sec = sec + 20; pi->entry[i+1].sec = sec + 40; - pi->nr = lastindex+1; pi->maxtime = pi->entry[lastindex].sec; - pi->minpressure = dive->cylinder[0].end.mbar; + pi->endpressure = pi->minpressure = dive->cylinder[0].end.mbar; pi->maxpressure = dive->cylinder[0].start.mbar; pi->meandepth = dive->meandepth.mm; + if (missing_pr) { + fill_missing_tank_pressures(dive, pi, track_pr); + } + for (cyl = 0; cyl < MAX_CYLINDERS; cyl++) + list_free(track_pr[cyl]); return analyze_plot_info(pi); } -void plot(struct graphics_context *gc, int w, int h, struct dive *dive) +void plot(struct graphics_context *gc, cairo_rectangle_int_t *drawing_area, struct dive *dive) { - double topx, topy; - struct plot_info *pi = create_plot_info(dive); + struct plot_info *pi; + static struct sample fake[4]; + struct sample *sample = dive->sample; + int nr = dive->samples; + + if (!nr) { + int duration = dive->duration.seconds; + int maxdepth = dive->maxdepth.mm; + sample = fake; + fake[1].time.seconds = duration * 0.05; + fake[1].depth.mm = maxdepth; + fake[2].time.seconds = duration * 0.95; + fake[2].depth.mm = maxdepth; + fake[3].time.seconds = duration * 1.00; + nr = 4; + } + + pi = create_plot_info(dive, nr, sample); - topx = w / 20.0; - topy = h / 20.0; - cairo_translate(gc->cr, topx, topy); + cairo_translate(gc->cr, drawing_area->x, drawing_area->y); cairo_set_line_width(gc->cr, 2); cairo_set_line_cap(gc->cr, CAIRO_LINE_CAP_ROUND); cairo_set_line_join(gc->cr, CAIRO_LINE_JOIN_ROUND); @@ -733,8 +1121,8 @@ void plot(struct graphics_context *gc, int w, int h, struct dive *dive) * * Snif. What a pity. */ - gc->maxx = (w - 2*topx); - gc->maxy = (h - 2*topy); + gc->maxx = (drawing_area->width - 2*drawing_area->x); + gc->maxy = (drawing_area->height - 2*drawing_area->y); /* Temperature profile */ plot_temperature_profile(gc, pi);