+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;
+ }
+ for (i = 0; i < dive->samples; i++) {
+ entry = pi->entry + i + 2;
+ 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);
+ }
+ }
+ }
+}
+
+/*
+ * Create a plot-info with smoothing and ranged min/max
+ *
+ * This also makes sure that we have extra empty events on both
+ * sides, so that you can do end-points without having to worry
+ * about it.
+ */
+static struct plot_info *create_plot_info(struct dive *dive)
+{
+ int cylinderindex = -1;
+ int lastdepth, lastindex;
+ int i, nr = dive->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;
+
+ pi = malloc(alloc_size);
+ if (!pi)
+ return pi;
+ memset(pi, 0, alloc_size);
+ pi->nr = nr;
+ sec = 0;
+ lastindex = 0;
+ lastdepth = -1;
+ 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[dive->sample[0].cylinderindex];
+ for (i = 0; i < dive->samples; i++) {
+ int depth;
+ struct sample *sample = dive->sample+i;
+
+ entry = pi->entry + i + 2;
+ sec = entry->sec = sample->time.seconds;
+ depth = entry->depth = sample->depth.mm;
+ entry->same_cylinder = sample->cylinderindex == cylinderindex;
+ entry->cylinderindex = cylinderindex = sample->cylinderindex;
+ SENSOR_PRESSURE(entry) = sample->cylinderpressure.mbar;
+ /* 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);
+ entry->temperature = sample->temperature.mkelvin;
+
+ if (depth || lastdepth)
+ lastindex = i+2;
+
+ lastdepth = depth;
+ if (depth > pi->maxdepth)
+ pi->maxdepth = depth;
+ }
+ 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;
+ }
+ }
+ if (lastdepth)
+ lastindex = i + 2;
+ /* Fill in the last two entries with empty values but valid times */
+ i = dive->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->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, cairo_rectangle_int_t *drawing_area, struct dive *dive)
+{
+ struct plot_info *pi = create_plot_info(dive);
+
+ 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);
+
+ /*
+ * We can use "cairo_translate()" because that doesn't
+ * scale line width etc. But the actual scaling we need
+ * do set up ourselves..
+ *
+ * Snif. What a pity.
+ */
+ 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);
+
+ /* Cylinder pressure plot */
+ plot_cylinder_pressure(gc, pi);
+
+ /* Depth profile */
+ plot_depth_profile(gc, pi);
+ plot_events(gc, pi, dive);
+
+ /* Text on top of all graphs.. */
+ plot_temperature_text(gc, pi);
+ plot_depth_text(gc, pi);
+ plot_cylinder_pressure_text(gc, pi);
+
+ /* Bounding box last */
+ gc->leftx = 0; gc->rightx = 1.0;
+ gc->topy = 0; gc->bottomy = 1.0;
+
+ set_source_rgb(gc, 1, 1, 1);
+ move_to(gc, 0, 0);
+ line_to(gc, 0, 1);
+ line_to(gc, 1, 1);
+ line_to(gc, 1, 0);
+ cairo_close_path(gc->cr);
+ cairo_stroke(gc->cr);
+
+ free(pi);