[PATCH 1/2] Add Variance filter

Nelson Castillo nelsoneci at gmail.com
Wed Dec 3 10:54:14 CET 2008


This filter is useful to reject clicks that are not reliable. We
 only care about what happens when we receive DOWN events for the fist time.
 If this filter does not reject the first samples then it will change
 its internal state to "passed" and the remaining samples
 will be sent to the next filter in the chain.

 First we collect N samples, then then we sort them. We discard the borders
 of the vector (with a window) and then compute the variance of the remaining
 set. If the computed variance is bigger than a threshold, we reject the click.

 The parameters of the filter are:

 * size
 * threshold /* maximum allowed variance */
 * window

 I'm using values I tuned by trial an error. I noticed we discard a lot
 of bugus clicks. In fact, it seems harder to get them now.

Signed-off-by: Nelson Castillo <nelsoneci at gmail.com>
---

 arch/arm/configs/gta02-moredrivers-defconfig   |    1 
 arch/arm/mach-s3c2440/mach-gta02.c             |   18 ++
 drivers/input/touchscreen/Kconfig              |    9 +
 drivers/input/touchscreen/Makefile             |    1 
 drivers/input/touchscreen/s3c2410_ts.c         |   45 +++--
 drivers/input/touchscreen/ts_filter_variance.c |  205 ++++++++++++++++++++++++
 include/linux/ts_filter_variance.h             |   36 ++++
 7 files changed, 289 insertions(+), 26 deletions(-)
 create mode 100644 drivers/input/touchscreen/ts_filter_variance.c
 create mode 100644 include/linux/ts_filter_variance.h

diff --git a/arch/arm/configs/gta02-moredrivers-defconfig b/arch/arm/configs/gta02-moredrivers-defconfig
index fd9cfc9..050d1cb 100644
--- a/arch/arm/configs/gta02-moredrivers-defconfig
+++ b/arch/arm/configs/gta02-moredrivers-defconfig
@@ -981,6 +981,7 @@ CONFIG_INPUT_TOUCHSCREEN=y
 CONFIG_TOUCHSCREEN_FILTER=y
 CONFIG_TOUCHSCREEN_FILTER_MEDIAN=y
 CONFIG_TOUCHSCREEN_FILTER_MEAN=y
+CONFIG_TOUCHSCREEN_FILTER_VARIANCE=y
 # CONFIG_TOUCHSCREEN_ADS7846 is not set
 # CONFIG_TOUCHSCREEN_FUJITSU is not set
 CONFIG_TOUCHSCREEN_S3C2410=y
diff --git a/arch/arm/mach-s3c2440/mach-gta02.c b/arch/arm/mach-s3c2440/mach-gta02.c
index 61713af..91e9927 100644
--- a/arch/arm/mach-s3c2440/mach-gta02.c
+++ b/arch/arm/mach-s3c2440/mach-gta02.c
@@ -105,6 +105,7 @@
 
 #include <linux/ts_filter_mean.h>
 #include <linux/ts_filter_median.h>
+#include <linux/ts_filter_variance.h>
 
 /* arbitrates which sensor IRQ owns the shared SPI bus */
 static spinlock_t motion_irq_lock;
@@ -1015,6 +1016,13 @@ static struct s3c2410_udc_mach_info gta02_udc_cfg = {
 
 /* touchscreen configuration */
 
+static struct ts_filter_variance_configuration gta02_ts_variance_config = {
+	.extent = 20,
+	.window = 5,
+	.threshold = 10,	/* variance = 10, std = 3.1623 */
+	.attempts = 5,		/* try 5 times before giving up */
+};
+
 static struct ts_filter_median_configuration gta02_ts_median_config = {
 	.extent = 31,
 	.decimation_below = 5,
@@ -1031,12 +1039,14 @@ static struct s3c2410_ts_mach_info gta02_ts_cfg = {
 	.delay = 10000,
 	.presc = 0xff, /* slow as we can go */
 	.filter_sequence = {
-		[0] = &ts_filter_median_api,
-		[1] = &ts_filter_mean_api,
+		[0] = &ts_filter_variance_api,
+		[1] = &ts_filter_median_api,
+		[2] = &ts_filter_mean_api,
 	},
 	.filter_config = {
-		[0] = &gta02_ts_median_config,
-		[1] = &gta02_ts_mean_config,
+		[0] = &gta02_ts_variance_config,
+		[1] = &gta02_ts_median_config,
+		[2] = &gta02_ts_mean_config,
 	},
 };
 
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index ec28494..98566fd 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -21,6 +21,15 @@ menuconfig TOUCHSCREEN_FILTER
 
 if TOUCHSCREEN_FILTER
 
+config TOUCHSCREEN_FILTER_VARIANCE
+	bool "Variance Touchscreen Filter"
+	depends on INPUT_TOUCHSCREEN && TOUCHSCREEN_FILTER
+	default Y
+	help
+	  Say Y here if you want to use the Variance touchscreen filter, it
+	  helps discarding a click if we get too much noise.
+
+
 config TOUCHSCREEN_FILTER_MEDIAN
 	bool "Median Average Touchscreen Filter"
 	depends on INPUT_TOUCHSCREEN && TOUCHSCREEN_FILTER
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 1bbe8f5..ee6bd28 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -33,5 +33,6 @@ wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9713)	+= wm9713.o
 obj-$(CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE)	+= mainstone-wm97xx.o
 obj-$(CONFIG_TOUCHSCREEN_S3C2410)	+= s3c2410_ts.o
 obj-$(CONFIG_TOUCHSCREEN_FILTER)	+= ts_filter.o
+obj-$(CONFIG_TOUCHSCREEN_FILTER_VARIANCE)	+= ts_filter_variance.o
 obj-$(CONFIG_TOUCHSCREEN_FILTER_MEDIAN)	+= ts_filter_median.o
 obj-$(CONFIG_TOUCHSCREEN_FILTER_MEAN)	+= ts_filter_mean.o
diff --git a/drivers/input/touchscreen/s3c2410_ts.c b/drivers/input/touchscreen/s3c2410_ts.c
index a880163..ea0f58a 100644
--- a/drivers/input/touchscreen/s3c2410_ts.c
+++ b/drivers/input/touchscreen/s3c2410_ts.c
@@ -103,7 +103,7 @@ static char *s3c2410ts_name = "s3c2410 TouchScreen";
 #define TS_STATE_RELEASE 4
 
 #define SKIP_NHEAD 2
-#define SKIP_NTAIL 2
+#define SKIP_NTAIL 1
 
 /*
  * Per-touchscreen data.
@@ -392,27 +392,28 @@ static irqreturn_t stylus_action(int irq, void *dev_id)
 	ts.coords[1] = readl(base_addr + S3C2410_ADCDAT1) &
 						    S3C2410_ADCDAT1_YPDATA_MASK;
 
-	if (!ts.tsf[0]) /* filtering is disabled then use raw directly */
-		goto real_sample;
-
-	/* send it to the chain of filters */
-	if ((ts.tsf[0]->api->process)(ts.tsf[0], &ts.coords[0]))
-		goto real_sample;
-
-	/*
-	 * no real sample came out of processing yet,
-	 * get another raw result to feed it
-	 */
-
-	s3c2410_ts_start_adc_conversion();
-
-	return IRQ_HANDLED;
-
-real_sample:
-
-	if (ts.tsf[0])
-		(ts.tsf[0]->api->scale)(ts.tsf[0], &ts.coords[0]);
+	if (ts.tsf[0]) { /* filtering is enabled, don't use raw directly */
+		switch ((ts.tsf[0]->api->process)(ts.tsf[0], &ts.coords[0])) {
+		case 0:	/*
+			 * no real sample came out of processing yet,
+			 * get another raw result to feed it
+			 */
+			s3c2410_ts_start_adc_conversion();
+			return IRQ_HANDLED;
+		case 1:	/* filters are ready to deliver a sample */
+			(ts.tsf[0]->api->scale)(ts.tsf[0], &ts.coords[0]);
+			break;
+		case -1:
+			/* error in filters, ignore the event */
+			(ts.tsf[0]->api->clear)(ts.tsf[0]);
+			writel(WAIT4INT(1), base_addr + S3C2410_ADCTSC);
+			return IRQ_HANDLED;
+		default:
+			printk(KERN_ERR":stylus_action error\n");
+		}
+	}
 
+	/* We use a buffer because want an atomic operation */
 	buf[0] = 'P';
 	buf[1] = ts.coords[0];
 	buf[2] = ts.coords[1];
@@ -420,7 +421,7 @@ real_sample:
 	if (unlikely(__kfifo_put(ts.event_fifo, (unsigned char *)buf,
 		     sizeof(int) * 3) != sizeof(int) * 3))
 		/* should not happen */
-		printk(KERN_ERR __FILE__": stylus_action lost event!\n");
+			printk(KERN_ERR":stylus_action error\n");
 
 	writel(WAIT4INT(1), base_addr + S3C2410_ADCTSC);
 	mod_timer(&event_send_timer, jiffies + 1);
diff --git a/drivers/input/touchscreen/ts_filter_variance.c b/drivers/input/touchscreen/ts_filter_variance.c
new file mode 100644
index 0000000..c335210
--- /dev/null
+++ b/drivers/input/touchscreen/ts_filter_variance.c
@@ -0,0 +1,205 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Copyright (C) 2008 by Openmoko, Inc.
+ * Author: Nelson Castillo <arhuaco at freaks-unidos.net>
+ * All rights reserved.
+ *
+ * This filter is useful to reject clicks that are not reliable. We
+ * only care about what happens when we receive DOWN events for the fist time.
+ * If this filter does not reject the first samples then it will change
+ * its internal state to "passed" and the remaining samples
+ * will be passed to the next filter in the chain.
+ *
+ * First we collect N samples, then then we sort them. We discard the borders
+ * (with a window) and then compute the variance of the remaining set.
+ * If the computed variance is bigger than a threshold, we reject the click.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/sort.h>
+#include <linux/ts_filter_variance.h>
+
+static void ts_filter_variance_clear_internal(struct ts_filter_variance *tsfv,
+					      int attempts)
+{
+	tsfv->N = 0;
+	tsfv->passed = 0;
+	tsfv->tries_left = attempts;
+}
+
+static void ts_filter_variance_clear(struct ts_filter *tsf)
+{
+	struct ts_filter_variance *tsfv = (struct ts_filter_variance *)tsf;
+
+	ts_filter_variance_clear_internal(tsfv, tsfv->config->attempts);
+
+	if (tsf->next) /* chain */
+		(tsf->next->api->clear)(tsf->next);
+}
+
+static struct ts_filter *ts_filter_variance_create(void *conf, int count_coords)
+{
+	struct ts_filter_variance *tsfv;
+	int i;
+
+	BUG_ON((count_coords < 1) || (count_coords > MAX_TS_FILTER_COORDS));
+
+	tsfv = kzalloc(sizeof(struct ts_filter_variance), GFP_KERNEL);
+	if (!tsfv)
+		return NULL;
+
+	tsfv->config = (struct ts_filter_variance_configuration *)conf;
+	tsfv->tsf.count_coords = count_coords;
+
+	BUG_ON(tsfv->config->attempts <= 0);
+
+	tsfv->samples[0] = kmalloc(count_coords * sizeof(int) *
+				   tsfv->config->extent, GFP_KERNEL);
+	if (!tsfv->samples[0]) {
+		kfree(tsfv);
+		return NULL;
+	}
+	for (i = 1; i < count_coords; ++i)
+		tsfv->samples[i] = tsfv->samples[0] + i * tsfv->config->extent;
+
+	ts_filter_variance_clear_internal(tsfv, tsfv->config->attempts);
+
+	printk(KERN_INFO"  Created Variance ts filter len %d depth %d window"
+			" %d thresh %d\n", tsfv->config->extent,
+			count_coords, tsfv->config->window,
+			tsfv->config->threshold);
+
+	/* scale the threshold to avoid divisions later */
+	tsfv->config->threshold *= tsfv->config->extent -
+				   (tsfv->config->window << 1);
+
+	return &tsfv->tsf;
+}
+
+static void ts_filter_variance_destroy(struct ts_filter *tsf)
+{
+	struct ts_filter_variance *tsfv = (struct ts_filter_variance *)tsf;
+
+	kfree(tsfv->samples[0]); /* first guy has pointer from kmalloc */
+	kfree(tsf);
+}
+
+static void ts_filter_variance_scale(struct ts_filter *tsf, int *coords)
+{
+	struct ts_filter_variance *tsfv = (struct ts_filter_variance *)tsf;
+
+	if (!tsfv->passed)
+		return;
+
+	if (tsf->next) {
+		(tsf->next->api->scale)(tsf->next, coords);
+	} else {
+		int n;
+		for (n = 0; n < tsf->count_coords; n++) {
+			int c = tsfv->samples[n][tsfv->N / 2] +
+				tsfv->samples[n][tsfv->N / 2 + 1] +
+				tsfv->samples[n][tsfv->N / 2 - 1];
+			coords[n] = (c + 2) / 3;
+		}
+	}
+}
+
+static int int_cmp(const void *_a, const void *_b)
+{
+	const int *a = _a;
+	const int *b = _b;
+
+	if (*a > *b)
+		return 1;
+	if (*a < *b)
+		return -1;
+	return 0;
+}
+
+/* give us the raw sample data coords, and if we return 1 then you can
+ * get a filtered coordinate from coords: if we return 0 you didn't
+ * fill all the filters with samples yet.
+ */
+
+static int ts_filter_variance_process(struct ts_filter *tsf, int *coords)
+{
+	struct ts_filter_variance *tsfv = (struct ts_filter_variance *)tsf;
+	int n;
+
+	if (tsfv->passed) { /* chain */
+		if (tsf->next)
+			return (tsf->next->api->process)(tsf->next, coords);
+		return 1;
+	}
+
+	for (n = 0; n < tsf->count_coords; n++)
+		tsfv->samples[n][tsfv->N] = coords[n];
+
+	if (++tsfv->N < tsfv->config->extent)
+		return 0;	/* we meed more samples */
+
+	tsfv->passed = 1;
+
+	for (n = 0; n < tsfv->tsf.count_coords; n++) {
+		int i;
+		int avg = 0;
+		int variance = 0;
+
+		sort(tsfv->samples[n], tsfv->config->extent, sizeof(int),
+		     int_cmp, NULL);
+
+		for (i = tsfv->config->window; i < tsfv->config->extent -
+					       tsfv->config->window; ++i)
+			avg += tsfv->samples[n][i];
+
+		avg /= tsfv->config->extent - (tsfv->config->window << 1);
+
+		for (i = tsfv->config->window; i < tsfv->config->extent -
+						   tsfv->config->window; ++i) {
+			int s = tsfv->samples[n][i] - avg;
+			variance += s * s;
+		}
+
+		if (variance > tsfv->config->threshold) {
+			tsfv->passed = 0;
+			break;
+		}
+	}
+
+	if (tsfv->passed) /* Let's reuse the last sample */
+		return ts_filter_variance_process(tsf, coords);
+
+	if (--tsfv->tries_left) {
+		ts_filter_variance_clear_internal(tsfv, tsfv->tries_left);
+		return 0; /* ask for more samples */
+	}
+
+	/* avoid overflow if we are called again without clearing the filter */
+	ts_filter_variance_clear_internal(tsfv, tsfv->config->attempts);
+
+	return -1;
+}
+
+struct ts_filter_api ts_filter_variance_api = {
+	.create = ts_filter_variance_create,
+	.destroy = ts_filter_variance_destroy,
+	.clear = ts_filter_variance_clear,
+	.process = ts_filter_variance_process,
+	.scale = ts_filter_variance_scale,
+};
+
diff --git a/include/linux/ts_filter_variance.h b/include/linux/ts_filter_variance.h
new file mode 100644
index 0000000..574cf90
--- /dev/null
+++ b/include/linux/ts_filter_variance.h
@@ -0,0 +1,36 @@
+#ifndef __TS_FILTER_VARIANCE_H__
+#define __TS_FILTER_VARIANCE_H__
+
+#include <linux/ts_filter.h>
+
+/*
+ * touchscreen filter
+ *
+ * Variance
+ *
+ * Copyright (C) 2008 by Openmoko, Inc.
+ * Author: Nelson Castillo <arhuaco at freaks-unidos.net>
+ *
+ */
+
+struct ts_filter_variance_configuration {
+	int extent;
+	int window;
+	int threshold;
+	int attempts;
+};
+
+struct ts_filter_variance {
+	struct ts_filter tsf;
+	struct ts_filter_variance_configuration *config;
+
+	int *samples[2];
+	int N;			/* How many samples we have */
+
+	int tries_left;		/* How many times we can try to get a point */
+	int passed;		/* Did the samples pass the test? */
+};
+
+extern struct ts_filter_api ts_filter_variance_api;
+
+#endif




More information about the openmoko-kernel mailing list