[PATCH 7/8] Remove sort call from group filter

Nelson Castillo arhuaco at freaks-unidos.net
Tue Sep 22 12:23:43 CEST 2009


This patch applies upstream feedback to the group filter.
The algorithms are equivalent, thus we will get the same
results after applying this patch.

Signed-off-by: Nelson Castillo <arhuaco at freaks-unidos.net>
---

 drivers/input/touchscreen/ts_filter_group.c |  178 ++++++++++++++++-----------
 1 files changed, 106 insertions(+), 72 deletions(-)

diff --git a/drivers/input/touchscreen/ts_filter_group.c b/drivers/input/touchscreen/ts_filter_group.c
index b7c3f3d..93f8fc6 100644
--- a/drivers/input/touchscreen/ts_filter_group.c
+++ b/drivers/input/touchscreen/ts_filter_group.c
@@ -20,29 +20,39 @@
  *
  * This filter is useful to reject samples that are not reliable. We consider
  * that a sample is not reliable if it deviates form the Majority.
+ * This filter mixes information from all the available dimensions. It means
+ * that for two dimensions we draw a rectangle where the though-to-be good
+ * points can be found.
  *
- * 1) We collect S samples.
+ * The implementation would be more efficient with a double-linked list but
+ * let's keep it simple for now.
  *
- * 2) For each dimension:
- *
- *  - We sort the points.
+ * 1) We collect S samples and keep it in sorted sets.
  *  - Points that are "close enough" are considered to be in the same set.
- *  - We choose the set with more elements. If more than "threshold"
- *    points are in this set we use the first and the last point of the set
- *    to define the valid range for this dimension [min, max], otherwise we
- *    discard all the points and go to step 1.
+ *    We don't actually keep the sets but ranges of points.
+ *
+ * 2) For each dimension:
+ *  - We choose the range with more elements. If more than "threshold"
+ *    points are in this range we use the minimum and the maximum point
+ *    of the range to define the valid range for this dimension [min, max],
+ *    otherwise we discard all the points and the ranges and go to step 1.
  *
  * 3) We consider the unsorted S samples and try to feed them to the next
  *    filter in the chain. If one of the points of each sample
- *    is not in the allowed range for its dimension, we discard the sample.
+ *    is not in the allowed range for its dimension we discard the sample.
  *
  */
 
 #include <linux/kernel.h>
 #include <linux/slab.h>
-#include <linux/sort.h>
 #include "ts_filter_group.h"
 
+struct coord_range {
+	int min;	/* Minimum value of the range. */
+	int max;	/* Maximum value of the range  */
+	int N;		/* Number of points in the range. */
+};
+
 struct ts_filter_group {
 	/* Private filter configuration. */
 	struct ts_filter_group_configuration *config;
@@ -52,13 +62,15 @@ struct ts_filter_group {
 	int N;			/* How many samples we have. */
 	int *samples[MAX_TS_FILTER_COORDS];	/* The samples: our input. */
 
-	int *group_size;	/* Used for temporal computations. */
-	int *sorted_samples;	/* Used for temporal computations. */
+	/* Temporal values that help us compute range_min and range_max. */
+	struct coord_range *ranges[MAX_TS_FILTER_COORDS];	/* Ranges. */
+	int n_ranges[MAX_TS_FILTER_COORDS];		/* Number of ranges */
 
+	/* Computed ranges that help us filter the points. */
 	int range_max[MAX_TS_FILTER_COORDS];	/* Max. computed ranges. */
 	int range_min[MAX_TS_FILTER_COORDS];	/* Min. computed ranges. */
 
-	int tries_left;		/* We finish if we don't get enough samples. */
+	int tries_left;		/* We finish if we can't get enough samples. */
 	int ready;		/* If we are ready to deliver samples. */
 	int result;		/* Index of the point being returned. */
 };
@@ -70,10 +82,13 @@ struct ts_filter_group {
 static void ts_filter_group_clear_internal(struct ts_filter_group *tsfg,
 					   int attempts)
 {
+	int n;
 	tsfg->N = 0;
 	tsfg->tries_left = attempts;
 	tsfg->ready = 0;
 	tsfg->result = 0;
+	for (n = 0; n < tsfg->tsf.count_coords; n++)
+		tsfg->n_ranges[n] = 0;
 }
 
 static void ts_filter_group_clear(struct ts_filter *tsf)
@@ -101,8 +116,9 @@ static struct ts_filter *ts_filter_group_create(
 	tsfg->tsf.count_coords = count_coords;
 
 	BUG_ON(tsfg->config->attempts <= 0);
+	BUG_ON(tsfg->config->length < tsfg->config->threshold);
 
-	tsfg->samples[0] = kmalloc((2 + count_coords) * sizeof(int) *
+	tsfg->samples[0] = kmalloc(count_coords * sizeof(int) *
 				   tsfg->config->length, GFP_KERNEL);
 	if (!tsfg->samples[0]) {
 		kfree(tsfg);
@@ -110,10 +126,16 @@ static struct ts_filter *ts_filter_group_create(
 	}
 	for (i = 1; i < count_coords; ++i)
 		tsfg->samples[i] = tsfg->samples[0] + i * tsfg->config->length;
-	tsfg->sorted_samples = tsfg->samples[0] + count_coords *
-			       tsfg->config->length;
-	tsfg->group_size = tsfg->samples[0] + (1 + count_coords) *
-			       tsfg->config->length;
+
+	tsfg->ranges[0] = kmalloc(count_coords * sizeof(struct coord_range) *
+				  tsfg->config->length, GFP_KERNEL);
+	if (!tsfg->ranges[0]) {
+		kfree(tsfg->samples[0]);
+		kfree(tsfg);
+		return NULL;
+	}
+	for (i = 1; i < count_coords; ++i)
+		tsfg->ranges[i] = tsfg->ranges[0] + i * tsfg->config->length;
 
 	ts_filter_group_clear_internal(tsfg, tsfg->config->attempts);
 
@@ -128,77 +150,90 @@ static void ts_filter_group_destroy(struct ts_filter *tsf)
 {
 	struct ts_filter_group *tsfg = ts_filter_to_filter_group(tsf);
 
-	kfree(tsfg->samples[0]); /* first guy has pointer from kmalloc */
+	kfree(tsfg->samples[0]);
+	kfree(tsfg->ranges[0]);
 	kfree(tsf);
 }
 
-static int int_cmp(const void *_a, const void *_b)
-{
-	const int *a = _a;
-	const int *b = _b;
+static void ts_filter_group_prepare_next(struct ts_filter *tsf);
 
-	if (*a > *b)
-		return 1;
-	if (*a < *b)
-		return -1;
-	return 0;
-}
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#define IN_RANGE(c, r) ((c) >= (r).min - tsfg->config->close_enough && \
+			(c) <= (r).max + tsfg->config->close_enough)
 
-static void ts_filter_group_prepare_next(struct ts_filter *tsf);
+static void delete_spot(struct coord_range *v, int n, int size)
+{
+	int i;
+	for (i = n; i < size - 1; ++i)
+		v[i] = v[i + 1];
+}
 
 static int ts_filter_group_process(struct ts_filter *tsf, int *coords)
 {
 	struct ts_filter_group *tsfg = ts_filter_to_filter_group(tsf);
 	int n;
-	int i;
+	int j;
 
 	BUG_ON(tsfg->N >= tsfg->config->length);
 	BUG_ON(tsfg->ready);
 
-	for (n = 0; n < tsf->count_coords; n++)
-		tsfg->samples[n][tsfg->N] = coords[n];
+	for (n = 0; n < tsfg->tsf.count_coords; n++) {
+		int i;
+		struct coord_range *range = tsfg->ranges[n];
+		int *n_ranges = &tsfg->n_ranges[n];
+		int found = 0;
 
-	if (++tsfg->N < tsfg->config->length)
-		return 0;	/* We need more samples. */
+		tsfg->samples[n][tsfg->N] = coords[n];
 
-	for (n = 0; n < tsfg->tsf.count_coords; n++) {
-		int *v = tsfg->sorted_samples;
-		int ngroups = 0;
-		int best_size;
-		int best_idx = 0;
-		int idx = 0;
-
-		memcpy(v, tsfg->samples[n], tsfg->N * sizeof(int));
-		/*
-		 * FIXME: Remove this sort call. We already have the
-		 * algorithm for this modification. The filter will
-		 * need less points (about half) if there is not a
-		 * lot of noise. Right now we are doing a constant
-		 * amount of work no matter how much noise we are
-		 * dealing with.
-		 */
-		sort(v, tsfg->N, sizeof(int), int_cmp, NULL);
-
-		tsfg->group_size[0] = 1;
-		for (i = 1; i < tsfg->N; ++i) {
-			if (v[i] - v[i - 1] <= tsfg->config->close_enough)
-				tsfg->group_size[ngroups]++;
-			else
-				tsfg->group_size[++ngroups] = 1;
+		for (i = 0; i < *n_ranges; ++i) {
+			if (IN_RANGE(coords[n], range[i])) {
+				range[i].min  = MIN(range[i].min, coords[n]);
+				range[i].max  = MAX(range[i].max, coords[n]);
+				range[i].N++;
+				found = 1;
+				break;
+			} else if (coords[n] <= range[i].min)
+				break;	/* We need to insert a range. */
 		}
-		ngroups++;
-
-		best_size = tsfg->group_size[0];
-		for (i = 1; i < ngroups; i++) {
-			idx += tsfg->group_size[i - 1];
-			if (best_size < tsfg->group_size[i]) {
-				best_size = tsfg->group_size[i];
-				best_idx = idx;
+		if (found) { /* We might need to melt ranges. */
+			if (i && range[i - 1].max + tsfg->config->close_enough
+			    >= range[i].min) {
+				BUG_ON(range[i - 1].max >= range[i].max);
+				range[i - 1].max = range[i].max;
+				range[i - 1].N += range[i].N;
+				delete_spot(range, i, *n_ranges);
+				(*n_ranges)--;
+				i--;
+			}
+			if (i < *n_ranges - 1 && range[i + 1].min -
+			    tsfg->config->close_enough <= range[i].max) {
+				range[i].max = range[i + 1].max;
+				range[i].N += range[i + 1].N;
+				delete_spot(range, i + 1, *n_ranges);
+				(*n_ranges)--;
 			}
+		} else {
+			BUG_ON((*n_ranges) >= tsfg->config->length);
+			(*n_ranges)++;
+			for (j = *n_ranges - 1; j > i; --j)
+				range[j] = range[j - 1];
+			range[i].N = 1;
+			range[i].min = coords[n];
+			range[i].max = coords[n];
 		}
+	}
 
-		if (best_size < tsfg->config->threshold) {
-			/* This set is not good enough for us. */
+	if (++tsfg->N < tsfg->config->length)
+		return 0;
+
+	for (n = 0; n < tsfg->tsf.count_coords; ++n) {
+		int best = 0;
+		for (j = 1; j < tsfg->n_ranges[n]; ++j)
+			if (tsfg->ranges[n][best].N  < tsfg->ranges[n][j].N)
+				best = j;
+		if (tsfg->ranges[n][best].N < tsfg->config->threshold) {
+			/* This set of points is not good enough for us. */
 			if (--tsfg->tries_left) {
 				ts_filter_group_clear_internal
 					(tsfg, tsfg->tries_left);
@@ -207,9 +242,8 @@ static int ts_filter_group_process(struct ts_filter *tsf, int *coords)
 			}
 			return 1; /* We give up: error. */
 		}
-
-		tsfg->range_min[n] = v[best_idx];
-		tsfg->range_max[n] = v[best_idx + best_size - 1];
+		tsfg->range_min[n] = tsfg->ranges[n][best].min;
+		tsfg->range_max[n] = tsfg->ranges[n][best].max;
 	}
 
 	ts_filter_group_prepare_next(tsf);




More information about the openmoko-kernel mailing list