[PATCH 1/3] When merging dives, match up the cylinders to each other using gasmix

Linus Torvalds torvalds at linux-foundation.org
Thu Mar 28 10:14:02 PDT 2013


From: Linus Torvalds <torvalds at linux-foundation.org>
Date: Wed, 27 Mar 2013 19:04:46 -0700
Subject: [PATCH 1/3] When merging dives, match up the cylinders to each other using gasmix

.. so that different computers that have different ordering of the same
cylinders will see the end result the same way.

This also fixes up the sample sensor index and generates special initial
tank change events for the dive computers that had their cylinder
indexes renamed on them.

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

This is the same one I already sent yesterday, not resent as the first of 
a series of three patches, where the two other ones improve on our gasmix 
handling.

 dive.c    | 168 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
 dive.h    |   2 +
 profile.c |   2 +-
 3 files changed, 151 insertions(+), 21 deletions(-)

diff --git a/dive.c b/dive.c
index 0351b8ba6fdd..a43c1afc4292 100644
--- a/dive.c
+++ b/dive.c
@@ -986,31 +986,30 @@ static void merge_events(struct divecomputer *res, struct divecomputer *src1, st
 }
 
 /* Pick whichever has any info (if either). Prefer 'a' */
-static void merge_cylinder_type(cylinder_type_t *res, cylinder_type_t *a, cylinder_type_t *b)
-{
-	cylinder_type_t *clean = a;
-	if (a->size.mliter) {
-		clean = b;
-		b = a;
+static void merge_cylinder_type(cylinder_type_t *src, cylinder_type_t *dst)
+{
+	if (!dst->size.mliter)
+		dst->size.mliter = src->size.mliter;
+	if (!dst->workingpressure.mbar)
+		dst->workingpressure.mbar = src->workingpressure.mbar;
+	if (!dst->description) {
+		dst->description = src->description;
+		src->description = NULL;
 	}
-	if (clean->description)
-		free((void *)clean->description);
-	*res = *b;
 }
 
-static void merge_cylinder_mix(struct gasmix *res, struct gasmix *a, struct gasmix *b)
+static void merge_cylinder_mix(struct gasmix *src, struct gasmix *dst)
 {
-	if (a->o2.permille)
-		b = a;
-	*res = *b;
+	if (!dst->o2.permille)
+		*dst = *src;
 }
 
-static void merge_cylinder_info(cylinder_t *res, cylinder_t *a, cylinder_t *b)
+static void merge_cylinder_info(cylinder_t *src, cylinder_t *dst)
 {
-	merge_cylinder_type(&res->type, &a->type, &b->type);
-	merge_cylinder_mix(&res->gasmix, &a->gasmix, &b->gasmix);
-	MERGE_MAX(res, a, b, start.mbar);
-	MERGE_MIN(res, a, b, end.mbar);
+	merge_cylinder_type(&src->type, &dst->type);
+	merge_cylinder_mix(&src->gasmix, &dst->gasmix);
+	MERGE_MAX(dst, dst, src, start.mbar);
+	MERGE_MIN(dst, dst, src, end.mbar);
 }
 
 static void merge_weightsystem_info(weightsystem_t *res, weightsystem_t *a, weightsystem_t *b)
@@ -1020,12 +1019,141 @@ static void merge_weightsystem_info(weightsystem_t *res, weightsystem_t *a, weig
 	*res = *a;
 }
 
+static int gasmix_distance(const struct gasmix *a, const struct gasmix *b)
+{
+	int a_o2 = a->o2.permille ? : O2_IN_AIR;
+	int b_o2 = b->o2.permille ? : O2_IN_AIR;
+	int a_he = a->he.permille, b_he = b->he.permille;
+	int delta_o2 = a_o2 - b_o2, delta_he = a_he - b_he;
+
+	delta_he = delta_he*delta_he;
+	delta_o2 = delta_o2*delta_o2;
+	return delta_he + delta_o2;
+}
+
+static int find_cylinder_match(cylinder_t *cyl, cylinder_t array[], unsigned int used)
+{
+	int i;
+	int best = -1, score = INT_MAX;
+
+	if (cylinder_nodata(cyl))
+		return -1;
+	for (i = 0; i < MAX_CYLINDERS; i++) {
+		const cylinder_t *match;
+		int distance;
+
+		if (used & (1<<i))
+			continue;
+		match = array+i;
+		distance = gasmix_distance(&cyl->gasmix, &match->gasmix);
+		if (distance >= score)
+			continue;
+		best = i;
+		score = distance;
+	}
+	return best;
+}
+
+/* Force an initial gaschange event to the (old) gas #0 */
+static void add_initial_gaschange(struct dive *dive, struct divecomputer *dc)
+{
+	int o2, he, value;
+	struct event *ev = get_next_event(dc->events, "gaschange");
+
+	if (ev && ev->time.seconds < 30)
+		return;
+
+	/* Old starting gas mix */
+	o2 = dive->cylinder[0].gasmix.o2.permille ? : O2_IN_AIR;
+	he = dive->cylinder[0].gasmix.o2.permille;
+	o2 = (o2 + 5) / 10;
+	he = (he + 5) / 10;
+	value = o2 + (he << 16);
+
+	add_event(dc, 0, 11, 0, value, "gaschange");
+}
+
+static void dc_cylinder_renumber(struct dive *dive, struct divecomputer *dc, int mapping[])
+{
+	int i;
+
+	/* Did the first gas get remapped? Add gas switch event */
+	if (mapping[0] > 0)
+		add_initial_gaschange(dive, dc);
+
+	/* Remap the sensor indexes */
+	for (i = 0; i < dc->samples; i++) {
+		struct sample *s = dc->sample+i;
+		int sensor;
+
+		if (!s->cylinderpressure.mbar)
+			continue;
+		sensor = mapping[s->sensor];
+		if (sensor >= 0)
+			s->sensor = sensor;
+	}
+}
+
+/*
+ * If the cylinder indexes change (due to merging dives or deleting
+ * cylinders in the middle), we need to change the indexes in the
+ * dive computer data for this dive.
+ *
+ * Also note that we assume that the initial cylinder is cylinder 0,
+ * so if that got renamed, we need to create a fake gas change event
+ */
+static void cylinder_renumber(struct dive *dive, int mapping[])
+{
+	struct divecomputer *dc;
+
+	dc = &dive->dc;
+	do {
+		dc_cylinder_renumber(dive, dc, mapping);
+	} while ((dc = dc->next) != NULL);
+}
+
+/*
+ * Merging cylinder information is non-trivial, because the two dive computers
+ * may have different ideas of what the different cylinder indexing is.
+ *
+ * Logic: take all the cylinder information from the preferred dive ('a'), and
+ * then try to match each of the cylinders in the other dive by the gasmix that
+ * is the best match and hasn't been used yet.
+ */
+static void merge_cylinders(struct dive *res, struct dive *a, struct dive *b)
+{
+	int i, renumber = 0;
+	int mapping[MAX_CYLINDERS];
+	unsigned int used = 0;
+
+	/* Copy the cylinder info raw from 'a' */
+	memcpy(res->cylinder, a->cylinder, sizeof(res->cylinder));
+	memset(a->cylinder, 0, sizeof(a->cylinder));
+
+	for (i = 0; i < MAX_CYLINDERS; i++) {
+		int j;
+		cylinder_t *cyl = b->cylinder + i;
+
+		j = find_cylinder_match(cyl, res->cylinder, used);
+		mapping[i] = j;
+		if (j < 0)
+			continue;
+		used |= 1 << j;
+		merge_cylinder_info(cyl, res->cylinder+j);
+
+		/* If that renumbered the cylinders, fix it up! */
+		if (i != j)
+			renumber = 1;
+	}
+	if (renumber)
+		cylinder_renumber(b, mapping);
+}
+
 static void merge_equipment(struct dive *res, struct dive *a, struct dive *b)
 {
 	int i;
 
-	for (i = 0; i < MAX_CYLINDERS; i++)
-		merge_cylinder_info(res->cylinder+i, a->cylinder + i, b->cylinder + i);
+	merge_cylinders(res, a, b);
 	for (i = 0; i < MAX_WEIGHTSYSTEMS; i++)
 		merge_weightsystem_info(res->weightsystem+i, a->weightsystem + i, b->weightsystem + i);
 }
diff --git a/dive.h b/dive.h
index 9d359fe18e9c..25cad4bfc210 100644
--- a/dive.h
+++ b/dive.h
@@ -683,6 +683,8 @@ void add_gas_to_nth_dp(struct diveplan *diveplan, int idx, int o2, int he);
 void free_dps(struct divedatapoint *dp);
 void get_gas_string(int o2, int he, char *buf, int len);
 
+struct event *get_next_event(struct event *event, char *name);
+
 #ifdef DEBUGFILE
 extern char *debugfilename;
 extern FILE *debugfile;
diff --git a/profile.c b/profile.c
index a89e0402861c..bcba6c84054e 100644
--- a/profile.c
+++ b/profile.c
@@ -1533,7 +1533,7 @@ static int get_cylinder_index(struct dive *dive, struct event *ev)
 	return 0;
 }
 
-static struct event *get_next_event(struct event *event, char *name)
+struct event *get_next_event(struct event *event, char *name)
 {
 	if (!name || !*name)
 		return NULL;
-- 
1.8.2.230.g2bba2f0



More information about the subsurface mailing list