diff options
-rw-r--r-- | dive.h | 4 | ||||
-rw-r--r-- | gaspressures.c | 347 | ||||
-rw-r--r-- | gaspressures.h | 33 | ||||
-rw-r--r-- | profile.c | 347 | ||||
-rw-r--r-- | profile.h | 11 | ||||
-rw-r--r-- | subsurface.pro | 2 |
6 files changed, 396 insertions, 348 deletions
@@ -362,7 +362,7 @@ static inline int get_surface_pressure_in_mbar(const struct dive *dive, bool non /* Pa = N/m^2 - so we determine the weight (in N) of the mass of 10m * of water (and use standard salt water at 1.03kg per liter if we don't know salinity) * and add that to the surface pressure (or to 1013 if that's unknown) */ -static inline int calculate_depth_to_mbar(int depth, pressure_t surface_pressure, int salinity) +inline int calculate_depth_to_mbar(int depth, pressure_t surface_pressure, int salinity) { double specific_weight; int mbar = surface_pressure.mbar; @@ -376,7 +376,7 @@ static inline int calculate_depth_to_mbar(int depth, pressure_t surface_pressure return mbar; } -static inline int depth_to_mbar(int depth, struct dive *dive) +inline int depth_to_mbar(int depth, struct dive *dive) { return calculate_depth_to_mbar(depth, dive->surface_pressure, dive->salinity); } 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]); +} diff --git a/gaspressures.h b/gaspressures.h new file mode 100644 index 000000000..e48a32418 --- /dev/null +++ b/gaspressures.h @@ -0,0 +1,33 @@ +#ifndef GASPRESSURES_H +#define GASPRESSURES_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * 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; + int pressure_time; + pr_track_t *next; +}; + +typedef struct pr_interpolate_struct pr_interpolate_t; +struct pr_interpolate_struct { + int start; + int end; + int pressure_time; + int acc_pressure_time; +}; + +#ifdef __cplusplus +} +#endif +#endif // GASPRESSURES_H @@ -10,6 +10,7 @@ #include "divelist.h" #include "profile.h" +#include "gaspressures.h" #include "deco.h" #include "libdivecomputer/parser.h" #include "libdivecomputer/version.h" @@ -18,9 +19,13 @@ int selected_dive = -1; /* careful: 0 is a valid value */ unsigned int dc_number = 0; - static struct plot_data *last_pi_entry_new = NULL; +void fill_missing_segment_pressures(pr_track_t*); +struct pr_interpolate_struct get_pr_interpolate_data(pr_track_t*, struct plot_info*, int); +void fill_missing_tank_pressures(struct dive*, struct plot_info*, pr_track_t**); +void populate_pressure_information(struct dive*, struct divecomputer*, struct plot_info*); + #ifdef DEBUG_PI /* debugging tool - not normally used */ static void dump_pi(struct plot_info *pi) @@ -284,295 +289,6 @@ 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; - int 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->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 - -typedef struct pr_interpolate_struct pr_interpolate_t; -struct pr_interpolate_struct { - int start; - int end; - int pressure_time; - int acc_pressure_time; -}; - -#ifdef DEBUG_PR_INTERPOLATE -static 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 - -/* - * 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. - */ -static 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; - } -} - -/* - * 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. - */ -static inline int 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; -} - -static 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; -} - -static 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]; - } -} - int get_cylinder_index(struct dive *dive, struct event *ev) { int i; @@ -955,57 +671,6 @@ static void setup_gas_sensor_pressure(struct dive *dive, struct divecomputer *dc } while ((secondary = secondary->next) != NULL); } -static 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 = 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]); -} - /* calculate DECO STOP / TTS / NDL */ static void calculate_ndl_tts(double tissue_tolerance, struct plot_data *entry, struct dive *dive, double surface_pressure) { @@ -52,6 +52,12 @@ struct plot_data { int heartbeat; int bearing; }; + +struct ev_select { + char *ev_name; + bool plot_ev; +}; + struct plot_info calculate_max_limits_new(struct dive *dive, struct divecomputer *dc); void compare_samples(struct plot_data *e1, struct plot_data *e2, char *buf, int bufsize, int sum); struct plot_data *populate_plot_entries(struct dive *dive, struct divecomputer *dc, struct plot_info *pi); @@ -60,11 +66,6 @@ void create_plot_info_new(struct dive *dive, struct divecomputer *dc, struct plo void calculate_deco_information(struct dive *dive, struct divecomputer *dc, struct plot_info *pi, bool print_mode); void get_plot_details_new(struct plot_info *pi, int time, struct membuffer *); -struct ev_select { - char *ev_name; - bool plot_ev; -}; - /* * When showing dive profiles, we scale things to the * current dive. However, we don't scale past less than diff --git a/subsurface.pro b/subsurface.pro index 4ce0ebaaf..6e20df9c2 100644 --- a/subsurface.pro +++ b/subsurface.pro @@ -38,6 +38,7 @@ HEADERS = \ worldmap-options.h \ pref.h \ profile.h \ + gaspressures.h \ qt-gui.h \ qthelper.h \ units.h \ @@ -117,6 +118,7 @@ SOURCES = \ parse-xml.c \ planner.c \ profile.c \ + gaspressures.c \ divecomputer.cpp \ worldmap-save.c \ save-html.c \ |