mirror of
https://github.com/subsurface/subsurface.git
synced 2024-11-30 22:20:21 +00:00
Do a better job at creating plot info entries
This simplifies - and improves - the code to generate the plot info entries from the samples. We used to generate exactly one plot info entry per sample, and then - because the result doesn't have high enough granularity - we'd generate additional plot info entries at gas change events etc. Which resulted in all kinds of ugly special case logic. Not only for the gas switch, btw: you can see the effects of this in the deco graph (done at plot entry boundaries) and in the gas pressure curves. So this throws that "do special plot entries for gas switch events" code away entirely, and replaces it with a much more straightforward model: we generate plot entries at a minimum of ten-second intervals. If you have samples more often than that, you'll get more frequent plot entries, but you'll never get less than that "every ten seconds". As a result, the code is smaller and simpler (99 insertions, 161 deletions), and actually does a better job too. You can see the difference especially in the test dives that only have a few entries (or if you create a new dive without a dive computer, using the "Add Dive" menu entry). Look at the deco graph of test-dive 20 before and after, for example. You can also see it very subtly in the cylinder pressure curves going from line segments to curves on that same dive. Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
This commit is contained in:
parent
c8830ffe48
commit
d7925dac44
1 changed files with 99 additions and 161 deletions
260
profile.c
260
profile.c
|
@ -1461,19 +1461,6 @@ static void check_gas_change_events(struct dive *dive, struct divecomputer *dc,
|
|||
set_cylinder_index(pi, i, cylinderindex, ~0u);
|
||||
}
|
||||
|
||||
/* for computers that track gas changes through events */
|
||||
static int count_gas_change_events(struct divecomputer *dc)
|
||||
{
|
||||
int count = 0;
|
||||
struct event *ev = get_next_event(dc->events, "gaschange");
|
||||
|
||||
while (ev) {
|
||||
count++;
|
||||
ev = get_next_event(ev->next, "gaschange");
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
static void calculate_max_limits(struct dive *dive, struct divecomputer *dc, struct graphics_context *gc)
|
||||
{
|
||||
struct plot_info *pi;
|
||||
|
@ -1539,6 +1526,98 @@ static void calculate_max_limits(struct dive *dive, struct divecomputer *dc, str
|
|||
pi->maxtemp = maxtemp;
|
||||
}
|
||||
|
||||
/* Average between 'a' and 'b', when we are 'part'way into the 'whole' distance from a to b */
|
||||
static int average_depth(int a, int b, int part, int whole)
|
||||
{
|
||||
double x;
|
||||
|
||||
x = (a * (whole - part) + b * part) / whole;
|
||||
return rint(x);
|
||||
}
|
||||
|
||||
static struct plot_data *populate_plot_entries(struct dive *dive, struct divecomputer *dc, struct plot_info *pi)
|
||||
{
|
||||
int idx, maxtime, nr, i;
|
||||
int lastdepth, lasttime;
|
||||
struct plot_data *plot_data;
|
||||
|
||||
maxtime = get_maxtime(pi);
|
||||
if (dive->end > 0)
|
||||
maxtime = dive->end;
|
||||
|
||||
/*
|
||||
* We want to have a plot_info event at least every 10s (so "maxtime/10"),
|
||||
* but samples could be more dense than that (so add in dc->samples), and
|
||||
* additionally we want two surface events around the whole thing (thus the
|
||||
* additional 4).
|
||||
*/
|
||||
nr = dc->samples + 4 + maxtime / 10;
|
||||
plot_data = calloc(nr, sizeof(struct plot_data));
|
||||
pi->entry = plot_data;
|
||||
if (!plot_data)
|
||||
return NULL;
|
||||
pi->nr = nr;
|
||||
idx = 2; /* the two extra events at the start */
|
||||
|
||||
lastdepth = 0;
|
||||
lasttime = 0;
|
||||
for (i = 0; i < dc->samples; i++) {
|
||||
struct plot_data *entry = plot_data + idx;
|
||||
struct sample *sample = dc->sample+i;
|
||||
int time = sample->time.seconds;
|
||||
int depth = sample->depth.mm;
|
||||
int offset, delta;
|
||||
|
||||
/* Add intermediate plot entries if required */
|
||||
delta = time - lasttime;
|
||||
for (offset = 10; offset < delta; offset += 10) {
|
||||
if (lasttime + offset > maxtime)
|
||||
break;
|
||||
|
||||
/* Use the data from the previous plot entry */
|
||||
*entry = entry[-1];
|
||||
|
||||
/* .. but update depth and time, obviously */
|
||||
entry->sec = lasttime + offset;
|
||||
entry->depth = average_depth(lastdepth, depth, offset, delta);
|
||||
|
||||
/* And clear out the sensor pressure, since we'll interpolate */
|
||||
SENSOR_PRESSURE(entry) = 0;
|
||||
|
||||
idx++; entry++;
|
||||
}
|
||||
|
||||
if (time > maxtime)
|
||||
break;
|
||||
|
||||
entry->sec = time;
|
||||
entry->depth = depth;
|
||||
|
||||
entry->stopdepth = sample->stopdepth.mm;
|
||||
entry->stoptime = sample->stoptime.seconds;
|
||||
entry->ndl = sample->ndl.seconds;
|
||||
pi->has_ndl |= sample->ndl.seconds;
|
||||
entry->in_deco = sample->in_deco;
|
||||
entry->cns = sample->cns;
|
||||
entry->po2 = sample->po2 / 1000.0;
|
||||
/* FIXME! sensor index -> cylinder index translation! */
|
||||
entry->cylinderindex = sample->sensor;
|
||||
SENSOR_PRESSURE(entry) = sample->cylinderpressure.mbar;
|
||||
entry->temperature = sample->temperature.mkelvin;
|
||||
|
||||
lasttime = time;
|
||||
lastdepth = depth;
|
||||
idx++;
|
||||
}
|
||||
|
||||
/* Add two final surface events */
|
||||
plot_data[idx++].sec = lasttime+10;
|
||||
plot_data[idx++].sec = lasttime+20;
|
||||
pi->nr = idx;
|
||||
|
||||
return plot_data;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a plot-info with smoothing and ranged min/max
|
||||
*
|
||||
|
@ -1549,16 +1628,13 @@ static void calculate_max_limits(struct dive *dive, struct divecomputer *dc, str
|
|||
static struct plot_info *create_plot_info(struct dive *dive, struct divecomputer *dc, struct graphics_context *gc)
|
||||
{
|
||||
int cylinderindex = -1;
|
||||
int lastdepth, lastindex;
|
||||
int i, pi_idx, nr, sec, cyl, stoptime, ndl, stopdepth, cns;
|
||||
gboolean in_deco;
|
||||
int i, nr, cyl;
|
||||
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;
|
||||
double amb_pressure, po2;
|
||||
double amb_pressure;
|
||||
double surface_pressure = (dive->surface_pressure.mbar ? dive->surface_pressure.mbar : 1013) / 1000.0;
|
||||
|
||||
/* The plot-info is embedded in the graphics context */
|
||||
|
@ -1567,129 +1643,16 @@ static struct plot_info *create_plot_info(struct dive *dive, struct divecomputer
|
|||
/* reset deco information to start the calculation */
|
||||
init_decompression(dive);
|
||||
|
||||
/* we want to potentially add synthetic plot_info elements for the gas changes */
|
||||
nr = dc->samples + 4 + 2 * count_gas_change_events(dc);
|
||||
/* Create the new plot data */
|
||||
if (last_pi_entry)
|
||||
free((void *)last_pi_entry);
|
||||
last_pi_entry = pi->entry = calloc(nr, sizeof(struct plot_data));
|
||||
if (!pi->entry)
|
||||
return NULL;
|
||||
pi->nr = nr;
|
||||
pi_idx = 2; /* the two extra events at the start */
|
||||
/* check for gas changes before the samples start */
|
||||
ev = get_next_event(dc->events, "gaschange");
|
||||
while (ev && ev->time.seconds < dc->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_event(ev->next, "gaschange");
|
||||
}
|
||||
if (ev && ev->time.seconds == dc->sample->time.seconds) {
|
||||
/* we already have a sample at the time of the event */
|
||||
ev = get_next_event(ev->next, "gaschange");
|
||||
}
|
||||
sec = 0;
|
||||
lastindex = 0;
|
||||
lastdepth = -1;
|
||||
for (i = 0; i < dc->samples; i++) {
|
||||
int depth;
|
||||
int delay = 0;
|
||||
struct sample *sample = dc->sample+i;
|
||||
last_pi_entry = populate_plot_entries(dive, dc, pi);
|
||||
|
||||
if ((dive->start > -1 && sample->time.seconds < dive->start) ||
|
||||
(dive->end > -1 && sample->time.seconds > dive->end)) {
|
||||
pi_idx--;
|
||||
continue;
|
||||
}
|
||||
entry = pi->entry + i + pi_idx;
|
||||
in_deco = sample->in_deco;
|
||||
ndl = sample->ndl.seconds;
|
||||
pi->has_ndl |= ndl;
|
||||
stopdepth = sample->stopdepth.mm;
|
||||
stoptime = sample->stoptime.seconds;
|
||||
po2 = sample->po2 / 1000.0;
|
||||
cns = sample->cns;
|
||||
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;
|
||||
}
|
||||
/* we need a fake depth - let's interpolate */
|
||||
if (i) {
|
||||
entry->depth = sample->depth.mm -
|
||||
(sample->depth.mm - (sample-1)->depth.mm) / 2;
|
||||
} else
|
||||
entry->depth = sample->depth.mm;
|
||||
(entry + 1)->depth = entry->depth;
|
||||
entry->stopdepth = stopdepth;
|
||||
entry->stoptime = stoptime;
|
||||
entry->ndl = ndl;
|
||||
entry->cns = cns;
|
||||
entry->po2 = po2;
|
||||
entry->in_deco = in_deco;
|
||||
(entry + 1)->stopdepth = stopdepth;
|
||||
(entry + 1)->stoptime = stoptime;
|
||||
(entry + 1)->ndl = ndl;
|
||||
(entry + 1)->in_deco = in_deco;
|
||||
(entry + 1)->cns = cns;
|
||||
(entry + 1)->po2 = po2;
|
||||
pi_idx += 2;
|
||||
entry = pi->entry + i + pi_idx;
|
||||
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
|
||||
* just add a new one for the old tank and delay the
|
||||
* real even by one second (to keep time monotonous) */
|
||||
entry->sec = ev->time.seconds;
|
||||
entry->depth = sample->depth.mm;
|
||||
entry->stopdepth = stopdepth;
|
||||
entry->stoptime = stoptime;
|
||||
entry->ndl = ndl;
|
||||
entry->in_deco = in_deco;
|
||||
entry->cns = cns;
|
||||
entry->po2 = po2;
|
||||
pi_idx++;
|
||||
entry = pi->entry + i + pi_idx;
|
||||
ev = get_next_event(ev->next, "gaschange");
|
||||
delay = 1;
|
||||
}
|
||||
sec = entry->sec = sample->time.seconds + delay;
|
||||
depth = entry->depth = sample->depth.mm;
|
||||
entry->stopdepth = stopdepth;
|
||||
entry->stoptime = stoptime;
|
||||
entry->ndl = ndl;
|
||||
entry->in_deco = in_deco;
|
||||
entry->cns = cns;
|
||||
entry->po2 = po2;
|
||||
/* FIXME! sensor index -> cylinder index translation! */
|
||||
entry->cylinderindex = sample->sensor;
|
||||
SENSOR_PRESSURE(entry) = sample->cylinderpressure.mbar;
|
||||
entry->temperature = sample->temperature.mkelvin;
|
||||
|
||||
if (depth || lastdepth)
|
||||
lastindex = i + pi_idx;
|
||||
|
||||
lastdepth = depth;
|
||||
}
|
||||
entry = pi->entry + i + pi_idx;
|
||||
/* are there still unprocessed gas changes? that would be very strange */
|
||||
while (ev) {
|
||||
entry->sec = ev->time.seconds;
|
||||
entry->depth = 0; /* why are there gas changes after the dive is over? */
|
||||
pi_idx++;
|
||||
entry = pi->entry + i + pi_idx;
|
||||
ev = get_next_event(ev->next, "gaschange");
|
||||
}
|
||||
nr = dc->samples + pi_idx - 2;
|
||||
/* Populate the gas index from the gas change events */
|
||||
check_gas_change_events(dive, dc, pi);
|
||||
|
||||
nr = pi->nr-2;
|
||||
|
||||
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];
|
||||
|
@ -1778,24 +1741,6 @@ static struct plot_info *create_plot_info(struct dive *dive, struct divecomputer
|
|||
pr_track->end = pr;
|
||||
}
|
||||
}
|
||||
/* Fill in the last two entries with empty values but valid times
|
||||
* without creating a false cylinder change event */
|
||||
i = nr + 2;
|
||||
pi->entry[i].sec = sec + 20;
|
||||
pi->entry[i].same_cylinder = 1;
|
||||
pi->entry[i].cylinderindex = pi->entry[i-1].cylinderindex;
|
||||
INTERPOLATED_PRESSURE(pi->entry + i) = GET_PRESSURE(pi->entry + i - 1);
|
||||
amb_pressure = depth_to_mbar(pi->entry[i - 1].depth, dive) / 1000.0;
|
||||
pi->entry[i].po2 = pi->entry[i-1].po2 / amb_pressure;
|
||||
pi->entry[i].phe = pi->entry[i-1].phe / amb_pressure;
|
||||
pi->entry[i].pn2 = 1.01325 - pi->entry[i].po2 - pi->entry[i].phe;
|
||||
pi->entry[i+1].sec = sec + 40;
|
||||
pi->entry[i+1].same_cylinder = 1;
|
||||
pi->entry[i+1].cylinderindex = pi->entry[i-1].cylinderindex;
|
||||
INTERPOLATED_PRESSURE(pi->entry + i + 1) = GET_PRESSURE(pi->entry + i - 1);
|
||||
pi->entry[i+1].po2 = pi->entry[i].po2;
|
||||
pi->entry[i+1].phe = pi->entry[i].phe;
|
||||
pi->entry[i+1].pn2 = pi->entry[i].pn2;
|
||||
/* make sure the first two pi entries have a sane po2 / phe / pn2 */
|
||||
amb_pressure = depth_to_mbar(pi->entry[2].depth, dive) / 1000.0;
|
||||
if (pi->entry[1].po2 < 0.01)
|
||||
|
@ -1810,13 +1755,6 @@ static struct plot_info *create_plot_info(struct dive *dive, struct divecomputer
|
|||
pi->entry[0].phe = pi->entry[1].phe / amb_pressure;
|
||||
pi->entry[0].pn2 = 1.01325 - pi->entry[0].po2 - pi->entry[0].phe;
|
||||
|
||||
/* the number of actual entries - some computers have lots of
|
||||
* depth 0 samples at the end of a dive, we want to make sure
|
||||
* we have exactly one of them at the end */
|
||||
pi->nr = lastindex+1;
|
||||
while (pi->nr <= i+2 && pi->entry[pi->nr-1].depth > 0)
|
||||
pi->nr++;
|
||||
|
||||
pi->meandepth = dive->meandepth.mm;
|
||||
|
||||
if (missing_pr) {
|
||||
|
|
Loading…
Reference in a new issue