summaryrefslogtreecommitdiffstats
path: root/dive.c
diff options
context:
space:
mode:
authorGravatar Linus Torvalds <torvalds@linux-foundation.org>2012-12-04 12:26:28 -0800
committerGravatar Dirk Hohndel <dirk@hohndel.org>2012-12-04 21:19:02 -0800
commitfb0d728973712902afcf0640d4c5bbd0aabf0295 (patch)
tree5e9362b1850e79eed0402251596f592ddebc75c6 /dive.c
parentebd27b798a62d66d9f7e791e8e10fc8b30647f0a (diff)
downloadsubsurface-fb0d728973712902afcf0640d4c5bbd0aabf0295.tar.gz
Improve automatic dive merging logic
This tunes the heuristics for when to merge two dives together into a single dive. We used to just look at the date, and say "if they're within one minute of each other, try to merge". This looks at the actual dive data, and tries to see just how much sense it makes to merge the dive. It also checks if the dives to be merged use different dive computers, and if so relaxes the one minute to five, since most people aren't quite as OCD as I am, and don't tend to set their dive computers quite that exactly to the same time and date. I'm sure people can come up with other heuristics, but this should make that easier too. NOTE! If you have things like wrong timezones etc, and the divecomputer dates are thus off by hours rather than by a couple of minutes, this will still not merge them. For that kind of situation, we'd need some kind of manual merge option. Note that that is *not* the same as the current "merge two adjacent dives" together, which joins two separate dives into one *longer* dive with a surface interval in between. That kind of manual merge UI makes sense, but is independent of this partical change. 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.c108
1 files changed, 101 insertions, 7 deletions
diff --git a/dive.c b/dive.c
index af14f0fb8..d18a3d458 100644
--- a/dive.c
+++ b/dive.c
@@ -1052,19 +1052,113 @@ static int find_sample_offset(struct divecomputer *a, struct divecomputer *b)
#endif
/*
+ * Are a and b "similar" values, when given a reasonable lower end expected
+ * difference?
+ *
+ * So for example, we'd expect different dive computers to give different
+ * max depth readings. You might have them on different arms, and they
+ * have different pressure sensors and possibly different ideas about
+ * water salinity etc.
+ *
+ * So have an expected minimum difference, but also allow a larger relative
+ * error value.
+ */
+static int similar(unsigned long a, unsigned long b, unsigned long expected)
+{
+ if (a && b) {
+ unsigned long min, max, diff;
+
+ min = a; max = b;
+ if (a > b) {
+ min = b;
+ max = a;
+ }
+ diff = max - min;
+
+ /* Smaller than expected difference? */
+ if (diff < expected)
+ return 1;
+ /* Error less than 10% or the maximum */
+ if (diff*10 < max)
+ return 1;
+ }
+ return 0;
+}
+
+static int same_dive_computer(struct dive *a, struct dive *b)
+{
+ /* No model info in one or the other? Assume they're the same */
+ if (!a->dc.model || !b->dc.model)
+ return 1;
+ if (strcasecmp(a->dc.model, b->dc.model))
+ return 0;
+ /* No device ID? Assume same.. */
+ if (!a->dc.deviceid || !b->dc.deviceid)
+ return 1;
+ return 0;
+}
+
+/*
+ * Do we want to automatically try to merge two dives that
+ * look like they are the same dive?
+ *
+ * This happens quite commonly because you download a dive
+ * that you already had, or perhaps because you maintained
+ * multiple dive logs and want to load them all together
+ * (possibly one of them was imported from another dive log
+ * application entirely).
+ *
+ * NOTE! We mainly look at the dive time, but it can differ
+ * between two dives due to a few issues:
+ *
+ * - rounding the dive date to the nearest minute in other dive
+ * applications
+ *
+ * - dive computers with "relative datestamps" (ie the dive
+ * computer doesn't actually record an absolute date at all,
+ * but instead at download-time syncronizes its internal
+ * time with real-time on the downloading computer)
+ *
+ * - using multiple dive computers with different real time on
+ * the same dive
+ *
+ * We do not merge dives that look radically different, and if
+ * the dates are *too* far off the user will have to join two
+ * dives together manually. But this tries to handle the sane
+ * cases.
+ */
+static int likely_same_dive(struct dive *a, struct dive *b)
+{
+ int fuzz;
+
+ /*
+ * Do some basic sanity testing of the values we
+ * have filled in during 'fixup_dive()'
+ */
+ if (!similar(a->maxdepth.mm, b->maxdepth.mm, 1000) ||
+ !similar(a->meandepth.mm, b->meandepth.mm, 1000) ||
+ !similar(a->duration.seconds, b->duration.seconds, 5*60))
+ return 0;
+
+ /*
+ * Allow a minute difference by default (minute rounding etc),
+ * and more if the dive computers are clearly different.
+ */
+ fuzz = same_dive_computer(a, b) ? 60 : 5*60;
+
+ return ((a->when <= b->when + fuzz) && (a->when >= b->when - fuzz));
+}
+
+/*
* This could do a lot more merging. Right now it really only
* merges almost exact duplicates - something that happens easily
* with overlapping dive downloads.
*/
struct dive *try_to_merge(struct dive *a, struct dive *b, gboolean prefer_downloaded)
{
- /*
- * This assumes that the clocks on the dive computers are
- * roughly synchronized.
- */
- if ((a->when >= b->when + 60) || (a->when <= b->when - 60))
- return NULL;
- return merge_dives(a, b, 0, prefer_downloaded);
+ if (likely_same_dive(a, b))
+ return merge_dives(a, b, 0, prefer_downloaded);
+ return NULL;
}
static void free_events(struct event *ev)