summaryrefslogtreecommitdiffstats
path: root/dive.c
diff options
context:
space:
mode:
authorGravatar Linus Torvalds <torvalds@linux-foundation.org>2013-03-27 19:04:46 -0700
committerGravatar Dirk Hohndel <dirk@hohndel.org>2013-03-28 13:47:23 -0700
commitf2f29665d499b57a9f554f29898a300781670f14 (patch)
treeca5c9366db5adec811d62b9f6896175946b9b1c8 /dive.c
parentc083ec45a9f9a11bc29d1ad17a7c8316cd165a90 (diff)
downloadsubsurface-f2f29665d499b57a9f554f29898a300781670f14.tar.gz
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@linux-foundation.org> Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
Diffstat (limited to 'dive.c')
-rw-r--r--dive.c168
1 files changed, 148 insertions, 20 deletions
diff --git a/dive.c b/dive.c
index 0351b8ba6..a43c1afc4 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);
}