diff options
Diffstat (limited to 'gaspressures.c')
-rw-r--r-- | gaspressures.c | 347 |
1 files changed, 347 insertions, 0 deletions
diff --git a/gaspressures.c b/gaspressures.c new file mode 100644 index 000000000..b14264a3b --- /dev/null +++ b/gaspressures.c @@ -0,0 +1,347 @@ +/* gaspressures.c + * --------------- + * This file contains the routines to calculate the gas pressures in the cylinders. + * The functions below support the code in profile.c. + * The high-level function is populate_pressure_information(), called by function + * create_plot_info_new() in profile.c. The other functions below are, in turn, + * called by populate_pressure_information(). The calling sequence is as follows: + * + * populate_pressure_information() -> calc_pressure_time() + * -> fill_missing_tank_pressures() -> fill_missing_segment_pressures() + * -> get_pr_interpolate_data() + */ + +#include "gettext.h" +#include <limits.h> +#include <string.h> + +#include "dive.h" +#include "display.h" +#include "divelist.h" + +#include "profile.h" +#include "gaspressures.h" +#include "deco.h" +#include "libdivecomputer/parser.h" +#include "libdivecomputer/version.h" +#include "membuffer.h" + + +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->end = 0; + pt->t_start = pt->t_end = t_start; + pt->pressure_time = 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); +} + +#ifdef DEBUG_PR_TRACK +static void dump_pr_track(pr_track_t **track_pr) +{ + int cyl; + pr_track_t *list; + + for (cyl = 0; cyl < MAX_CYLINDERS; cyl++) { + list = track_pr[cyl]; + while (list) { + printf("cyl%d: start %d end %d t_start %d t_end %d pt %d\n", cyl, + list->start, list->end, list->t_start, list->t_end, list->pressure_time); + list = list->next; + } + } +} +#endif + +/* + * This looks at the pressures for one cylinder, and + * calculates any missing beginning/end pressures for + * each segment by taking the over-all SAC-rate into + * account for that cylinder. + * + * NOTE! Many segments have full pressure information + * (both beginning and ending pressure). But if we have + * switched away from a cylinder, we will have the + * beginning pressure for the first segment with a + * missing end pressure. We may then have one or more + * segments without beginning or end pressures, until + * we finally have a segment with an end pressure. + * + * We want to spread out the pressure over these missing + * segments according to how big of a time_pressure area + * they have. + */ +void fill_missing_segment_pressures(pr_track_t *list) +{ + while (list) { + int start = list->start, end; + pr_track_t *tmp = list; + int pt_sum = 0, pt = 0; + + for (;;) { + pt_sum += tmp->pressure_time; + end = tmp->end; + if (end) + break; + end = start; + if (!tmp->next) + break; + tmp = tmp->next; + } + + if (!start) + start = end; + + /* + * Now 'start' and 'end' contain the pressure values + * for the set of segments described by 'list'..'tmp'. + * pt_sum is the sum of all the pressure-times of the + * segments. + * + * Now dole out the pressures relative to pressure-time. + */ + list->start = start; + tmp->end = end; + for (;;) { + int pressure; + pt += list->pressure_time; + pressure = start; + if (pt_sum) + pressure -= (start - end) * (double)pt / pt_sum; + list->end = pressure; + if (list == tmp) + break; + list = list->next; + list->start = pressure; + } + + /* Ok, we've done that set of segments */ + list = list->next; + } +} + +#ifdef DEBUG_PR_INTERPOLATE +void dump_pr_interpolate(int i, pr_interpolate_t interpolate_pr) +{ + printf("Interpolate for entry %d: start %d - end %d - pt %d - acc_pt %d\n", i, + interpolate_pr.start, interpolate_pr.end, interpolate_pr.pressure_time, interpolate_pr.acc_pressure_time); +} +#endif + + +struct pr_interpolate_struct get_pr_interpolate_data(pr_track_t *segment, struct plot_info *pi, int cur) +{ + struct pr_interpolate_struct interpolate; + int i; + struct plot_data *entry; + + interpolate.start = segment->start; + interpolate.end = segment->end; + interpolate.acc_pressure_time = 0; + interpolate.pressure_time = 0; + + for (i = 0; i < pi->nr; i++) { + entry = pi->entry + i; + if (entry->sec < segment->t_start) + continue; + if (entry->sec >= segment->t_end) { + interpolate.pressure_time += entry->pressure_time; + break; + } + if (entry->sec == segment->t_start) { + interpolate.acc_pressure_time = 0; + interpolate.pressure_time = 0; + if (SENSOR_PRESSURE(entry)) + interpolate.start = SENSOR_PRESSURE(entry); + continue; + } + if (i < cur) { + if (SENSOR_PRESSURE(entry)) { + interpolate.start = SENSOR_PRESSURE(entry); + interpolate.acc_pressure_time = 0; + interpolate.pressure_time = 0; + } else { + interpolate.acc_pressure_time += entry->pressure_time; + interpolate.pressure_time += entry->pressure_time; + } + continue; + } + if (i == cur) { + interpolate.acc_pressure_time += entry->pressure_time; + interpolate.pressure_time += entry->pressure_time; + continue; + } + interpolate.pressure_time += entry->pressure_time; + if (SENSOR_PRESSURE(entry)) { + interpolate.end = SENSOR_PRESSURE(entry); + break; + } + } + return interpolate; +} + +void fill_missing_tank_pressures(struct dive *dive, struct plot_info *pi, pr_track_t **track_pr) +{ + int cyl, i; + struct plot_data *entry; + int cur_pr[MAX_CYLINDERS]; + +#ifdef DEBUG_PR_TRACK + /* another great debugging tool */ + dump_pr_track(track_pr); +#endif + for (cyl = 0; cyl < MAX_CYLINDERS; cyl++) { + if (!track_pr[cyl]) { + /* no segment where this cylinder is used */ + cur_pr[cyl] = -1; + continue; + } + fill_missing_segment_pressures(track_pr[cyl]); + cur_pr[cyl] = track_pr[cyl]->start; + } + + /* The first two are "fillers", but in case we don't have a sample + * at time 0 we need to process the second of them here */ + for (i = 1; i < pi->nr; i++) { + double magic; + pr_track_t *segment; + pr_interpolate_t interpolate; + + entry = pi->entry + i; + cyl = entry->cylinderindex; + + if (SENSOR_PRESSURE(entry)) { + cur_pr[cyl] = SENSOR_PRESSURE(entry); + continue; + } + + /* Find the right pressure segment for this entry.. */ + segment = track_pr[cyl]; + while (segment && segment->t_end < entry->sec) + segment = segment->next; + + /* No (or empty) segment? Just use our current pressure */ + if (!segment || !segment->pressure_time) { + SENSOR_PRESSURE(entry) = cur_pr[cyl]; + continue; + } + + interpolate = get_pr_interpolate_data(segment, pi, i); +#ifdef DEBUG_PR_INTERPOLATE + dump_pr_interpolate(i, interpolate); +#endif + /* if this segment has pressure time, calculate a new interpolated pressure */ + if (interpolate.pressure_time) { + /* Overall pressure change over total pressure-time for this segment*/ + magic = (interpolate.end - interpolate.start) / (double)interpolate.pressure_time; + + /* Use that overall pressure change to update the current pressure */ + cur_pr[cyl] = rint(interpolate.start + magic * interpolate.acc_pressure_time); + } + INTERPOLATED_PRESSURE(entry) = cur_pr[cyl]; + } +} + +/* + * What's the pressure-time between two plot data entries? + * We're calculating the integral of pressure over time by + * adding these up. + * + * The units won't matter as long as everybody agrees about + * them, since they'll cancel out - we use this to calculate + * a constant SAC-rate-equivalent, but we only use it to + * scale pressures, so it ends up being a unitless scaling + * factor. + */ +inline int calc_pressure_time(struct dive *dive, struct divecomputer *dc, struct plot_data *a, struct plot_data *b) +{ + int time = b->sec - a->sec; + int depth = (a->depth + b->depth) / 2; + + if (depth <= SURFACE_THRESHOLD) + return 0; + + return depth_to_mbar(depth, dive) * time; +} + +void populate_pressure_information(struct dive *dive, struct divecomputer *dc, struct plot_info *pi) +{ + int i, cylinderindex; + pr_track_t *track_pr[MAX_CYLINDERS] = { NULL, }; + pr_track_t *current; + bool missing_pr = false; + + cylinderindex = -1; + current = NULL; + for (i = 0; i < pi->nr; i++) { + struct plot_data *entry = pi->entry + i; + int pressure = SENSOR_PRESSURE(entry); + + /* discrete integration of pressure over time to get the SAC rate equivalent */ + if (current) { + entry->pressure_time = calc_pressure_time(dive, dc, entry - 1, entry); + current->pressure_time += entry->pressure_time; + current->t_end = entry->sec; + } + + /* track the segments per cylinder and their pressure/time integral */ + if (entry->cylinderindex != cylinderindex) { + cylinderindex = entry->cylinderindex; + current = pr_track_alloc(pressure, entry->sec); + track_pr[cylinderindex] = list_add(track_pr[cylinderindex], current); + continue; + } + + if (!pressure) { + missing_pr = 1; + continue; + } + + current->end = pressure; + + /* Was it continuous? */ + if (SENSOR_PRESSURE(entry - 1)) + continue; + + /* transmitter changed its working status */ + current = pr_track_alloc(pressure, entry->sec); + track_pr[cylinderindex] = list_add(track_pr[cylinderindex], current); + } + + if (missing_pr) { + fill_missing_tank_pressures(dive, pi, track_pr); + } + for (i = 0; i < MAX_CYLINDERS; i++) + list_free(track_pr[i]); +} |