Split up and re-organize the plot entry calculations

Linus Torvalds torvalds at linux-foundation.org
Sun Jan 6 16:22:52 PST 2013


From: Linus Torvalds <torvalds at linux-foundation.org>
Date: Sun, 6 Jan 2013 16:09:48 -0800
Subject: [PATCH] Split up and re-organize the plot entry calculations

This splits up the (very complex) function that calculates all the plot
info data, so that the gas pressure logic is in several helper
functions, and the deco and partial pressure calculations are in a
function of their own.

That makes the code almost readable.

This also changes the cylinder pressure calculations so that if you have
manually set the beginning and end pressures, those are the ones we will
show (by making them fake "sensor pressures").  We used to shopw some
random pressure that was related to the manually entered ones only
distantly (through various rounding phases and the SAC-rate calculations).

That does make the rounding errors more obvious in the graph, but we can
fix that separately.

Signed-off-by: Linus Torvalds <torvalds at linux-foundation.org>
---

Ok, this adds more lines than it removes, but much of it is because we 
split things up into smaller functions, so many lines are the function 
declaration etc whitespace that that causes.

Also, instead of doing many things in one loop (cylinder pressure tracking 
vs deco etc), it ends up doing the simple loop a couple of times, doing 
just one thing inside the loop. So each loop is much more obvious - but it 
does duplicate the loop itself, ie you have a few new separate

        for (i = 1; i < pi->nr; i++) {
                struct plot_data *entry = pi->entry + i;

things.

It also does do some actual new code in how it pre-populates the cylinder 
pressures into the plot samples. See populate_cylinder_pressure_data() and 
setup_gas_sensor_pressure(). Part of that is to help make the code more 
readable, part of it is so that if/when we start looking at multiple dive 
computer sensors (for different cylinder pressure info), we now have the 
infrastructure ready for that.

I do think that Lubomir is correct that we should split things up into 
more files, but quite frankly, I think the "one huge complex function" is 
more scary than "one big file". So this tries to fix that complex function 
thing.

 profile.c | 229 ++++++++++++++++++++++++++++++++++++++++++--------------------
 1 file changed, 155 insertions(+), 74 deletions(-)

diff --git a/profile.c b/profile.c
index 6e8102b70a80..7540ff764d45 100644
--- a/profile.c
+++ b/profile.c
@@ -1618,75 +1618,153 @@ static struct plot_data *populate_plot_entries(struct dive *dive, struct divecom
 	return plot_data;
 }
 
-/*
- * 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, struct divecomputer *dc, struct graphics_context *gc)
+static void populate_cylinder_pressure_data(int idx, int start, int end, struct plot_info *pi)
 {
-	int cylinderindex = -1;
-	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;
-	double amb_pressure;
-	double surface_pressure = (dive->surface_pressure.mbar ? dive->surface_pressure.mbar : 1013) / 1000.0;
+	int i;
 
-	/* The plot-info is embedded in the graphics context */
-	pi = &gc->pi;
+	/* First: check that none of the entries has sensor pressure for this cylinder index */
+	for (i = 0; i < pi->nr; i++) {
+		struct plot_data *entry = pi->entry+i;
+		if (entry->cylinderindex != idx)
+			continue;
+		if (SENSOR_PRESSURE(entry))
+			return;
+	}
 
-	/* reset deco information to start the calculation */
-	init_decompression(dive);
+	/* Then: populate the first entry with the beginning cylinder pressure */
+	for (i = 0; i < pi->nr; i++) {
+		struct plot_data *entry = pi->entry+i;
+		if (entry->cylinderindex != idx)
+			continue;
+		SENSOR_PRESSURE(entry) = start;
+		break;
+	}
 
-	/* Create the new plot data */
-	if (last_pi_entry)
-		free((void *)last_pi_entry);
-	last_pi_entry = populate_plot_entries(dive, dc, pi);
+	/* .. and the last entry with the ending cylinder pressure */
+	for (i = pi->nr; --i >= 0; ) {
+		struct plot_data *entry = pi->entry+i;
+		if (entry->cylinderindex != idx)
+			continue;
+		SENSOR_PRESSURE(entry) = end;
+		break;
+	}
+}
 
-	/* Populate the gas index from the gas change events */
-	check_gas_change_events(dive, dc, pi);
+static void populate_secondary_sensor_data(struct divecomputer *dc, struct plot_info *pi)
+{
+	/* We should try to see if it has interesting pressure data here */
+}
 
-	nr = pi->nr-2;
+static void setup_gas_sensor_pressure(struct dive *dive, struct divecomputer *dc, struct plot_info *pi)
+{
+	int i;
+	struct divecomputer *secondary;
 
-	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 + 1; i++) {
-		int fo2, fhe;
+	/* First, populate the pressures with the manual cylinder data.. */
+	for (i = 0; i < MAX_CYLINDERS; i++) {
+		cylinder_t *cyl = dive->cylinder+i;
+		int start = cyl->start.mbar ? : cyl->sample_start.mbar;
+		int end = cyl->end.mbar ? : cyl->sample_end.mbar;
+
+		if (!start || !end)
+			continue;
+
+		populate_cylinder_pressure_data(i, start, end, pi);
+	}
+
+	/*
+	 * Here, we should try to walk through all the dive computers,
+	 * and try to see if they have sensor data different from the
+	 * primary dive computer (dc).
+	 */
+	secondary = &dive->dc;
+	do {
+		if (secondary == dc)
+			continue;
+		populate_secondary_sensor_data(dc, pi);
+	} 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;
+	gboolean missing_pr = FALSE;
+
+	/* Set up the pressure tracking data structures */
+	for (i = 0; i < MAX_CYLINDERS; i++) {
+		cylinder_t *cyl = dive->cylinder + i;
+		int mbar = cyl->start.mbar ? : cyl->sample_start.mbar;
+		track_pr[i] = pr_track_alloc(mbar, 0);
+	}
+
+	cylinderindex = pi->entry[0].cylinderindex;
+	current = track_pr[cylinderindex];
+	for (i = 1; i < pi->nr; i++) {
+		struct plot_data *entry = pi->entry + i;
 
-		entry = pi->entry + i + 1;
+		entry = pi->entry + i;
 
 		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))) {
+			if (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->end = SENSOR_PRESSURE(entry);
+				current->t_end = entry->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) *
+			depth_to_mbar((entry->depth + (entry-1)->depth) / 2, dive) / 1000.0;
+		if (SENSOR_PRESSURE(entry)) {
+			current->end = SENSOR_PRESSURE(entry);
+			current->t_end = entry->sec;
+		}
+		missing_pr |= !SENSOR_PRESSURE(entry);
+	}
+
+	/* initialize the end pressures */
+	for (i = 0; i < MAX_CYLINDERS; i++) {
+		cylinder_t *cyl = dive->cylinder + i;
+		int pr = cyl->end.mbar ? : cyl->sample_end.mbar;
+		if (pr && track_pr[i]) {
+			pr_track_t *pr_track = list_last(track_pr[i]);
+			pr_track->end = pr;
+		}
+	}
+
+	if (missing_pr) {
+		fill_missing_tank_pressures(pi, track_pr);
+	}
+	for (i = 0; i < MAX_CYLINDERS; i++)
+		list_free(track_pr[i]);
+}
+
+static void calculate_deco_information(struct dive *dive, struct divecomputer *dc, struct plot_info *pi)
+{
+	int i;
+	double amb_pressure;
+	double surface_pressure = (dive->surface_pressure.mbar ? dive->surface_pressure.mbar : 1013) / 1000.0;
+
+	for (i = 1; i < pi->nr; i++) {
+		int fo2, fhe;
+		struct plot_data *entry = pi->entry + i;
+		int cylinderindex = entry->cylinderindex;
+
 		amb_pressure = depth_to_mbar(entry->depth, dive) / 1000.0;
-		fo2 = dive->cylinder[cylinderindex].gasmix.o2.permille;
+		fo2 = dive->cylinder[cylinderindex].gasmix.o2.permille ? : AIR_PERMILLE;
 		fhe = dive->cylinder[cylinderindex].gasmix.he.permille;
 
-		if (!fo2)
-			fo2 = AIR_PERMILLE;
 		if (entry->po2) {
 			/* we have an O2 partial pressure in the sample - so this
 			 * is likely a CC dive... use that instead of the value
@@ -1707,10 +1785,7 @@ static struct plot_info *create_plot_info(struct dive *dive, struct divecomputer
 			pi->maxpp = entry->phe;
 		if (entry->pn2 > pi->maxpp)
 			pi->maxpp = entry->pn2;
-		/* finally, do the discrete integration to get the SAC rate equivalent */
-		current->pressure_time += (entry->sec - (entry-1)->sec) *
-			depth_to_mbar((entry->depth + (entry-1)->depth) / 2, dive) / 1000.0;
-		missing_pr |= !SENSOR_PRESSURE(entry);
+
 		/* and now let's try to do some deco calculations */
 		if (i > 0) {
 			int j;
@@ -1727,41 +1802,47 @@ static struct plot_info *create_plot_info(struct dive *dive, struct divecomputer
 			entry->ceiling = deco_allowed_depth(tissue_tolerance, surface_pressure, dive, !prefs.calc_ceiling_3m_incr);
 		}
 	}
+
 #if DECO_CALC_DEBUG & 1
 	dump_tissues();
 #endif
+}
 
-	if (entry)
-		current->t_end = entry->sec;
+/*
+ * 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, struct divecomputer *dc, struct graphics_context *gc)
+{
+	struct plot_info *pi;
 
-	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;
-		}
-	}
-	/* 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)
-		pi->entry[1].po2 = pi->entry[2].po2 / amb_pressure;
-	if (pi->entry[1].phe < 0.01)
-		pi->entry[1].phe = pi->entry[2].phe / amb_pressure;
-	pi->entry[1].pn2 = 1.01325 - pi->entry[1].po2 - pi->entry[1].phe;
-	amb_pressure = depth_to_mbar(pi->entry[1].depth, dive) / 1000.0;
-	if (pi->entry[0].po2 < 0.01)
-		pi->entry[0].po2 = pi->entry[1].po2 / amb_pressure;
-	if (pi->entry[0].phe < 0.01)
-		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 plot-info is embedded in the graphics context */
+	pi = &gc->pi;
 
+	/* reset deco information to start the calculation */
+	init_decompression(dive);
+
+	/* Create the new plot data */
+	if (last_pi_entry)
+		free((void *)last_pi_entry);
+	last_pi_entry = populate_plot_entries(dive, dc, pi);
+
+	/* Populate the gas index from the gas change events */
+	check_gas_change_events(dive, dc, pi);
+
+	/* Try to populate our gas pressure knowledge */
+	setup_gas_sensor_pressure(dive, dc, pi);
+
+	/* .. calculate missing pressure entries */
+	populate_pressure_information(dive, dc, pi);
+
+	/* Then, calculate partial pressures and deco information */
+	calculate_deco_information(dive, dc, pi);
 	pi->meandepth = dive->meandepth.mm;
 
-	if (missing_pr) {
-		fill_missing_tank_pressures(pi, track_pr);
-	}
-	for (cyl = 0; cyl < MAX_CYLINDERS; cyl++)
-		list_free(track_pr[cyl]);
 	if (0) /* awesome for debugging - not useful otherwise */
 		dump_pi(pi);
 	return analyze_plot_info(pi);
-- 
1.8.1.rc2.6.g18499ba



More information about the subsurface mailing list