[PATCH] plugins: add IIR filter plugin

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Martin Kepplinger <martin.kepplinger@xxxxxxxxxxxxx>

Fixes #86
---
I found this very nice filter and thought why not implement it for us. It's
actually a must-have I think. It's really cheap in resourses and very effective,
and quite fun to play with. Choose 9/10 and see for yourself.

I'd be happy for feedback. Posted to the list for future reference too, to
demonstrate how to add a new filter.



 README.md            |  21 ++++
 configure.ac         |   1 +
 etc/ts.conf          |   3 +
 plugins/Makefile.am  |  14 +++
 plugins/iir.c        | 267 +++++++++++++++++++++++++++++++++++++++++++++++++++
 plugins/plugins.h    |   1 +
 src/Makefile.am      |   4 +
 src/ts_load_module.c |   3 +
 8 files changed, 314 insertions(+)
 create mode 100644 plugins/iir.c

diff --git a/README.md b/README.md
index 8e1423a..ff1dbb7 100644
--- a/README.md
+++ b/README.md
@@ -182,6 +182,27 @@ Parameters:
 	Maximum pressure value for a sample to be valid.
 
 
+### module: iir
+
+#### Description:
+  Infinite impulse response filter. Similar to dejitter, this is a smoothing
+  filter to remove low-level noise. There is a trade-off between noise removal
+  (smoothing) and responsiveness. The parameters N and D specify the level of
+  smoothing in the form of a fraction (N/D).
+
+  [Wikipedia](https://en.wikipedia.org/wiki/Infinite_impulse_response) has some
+  general theory.
+
+
+Parameters:
+* `N`
+
+	numerator of the smoothing fraction
+* `D`
+
+	denominator of the smoothing fraction
+
+
 ### module: dejitter
 
 #### Description:
diff --git a/configure.ac b/configure.ac
index 122b80e..153cbc1 100644
--- a/configure.ac
+++ b/configure.ac
@@ -97,6 +97,7 @@ TSLIB_CHECK_MODULE([median], [yes], [Enable building of median filter])
 TSLIB_CHECK_MODULE([pthres], [yes], [Enable building of pthres filter])
 TSLIB_CHECK_MODULE([debounce], [yes], [Enable building of debounce filter])
 TSLIB_CHECK_MODULE([skip], [yes], [Enable building of skip filter])
+TSLIB_CHECK_MODULE([iir], [yes], [Enable building of IIR filter])
 
 # hardware access modules
 if test "$build_linux" = "yes"; then
diff --git a/etc/ts.conf b/etc/ts.conf
index c63ff12..4f69b1a 100644
--- a/etc/ts.conf
+++ b/etc/ts.conf
@@ -44,6 +44,9 @@ module pthres pmin=1
 # Uncomment if needed to filter spikes
 # module median depth=5
 
+# Uncomment to enable smoothing of fraction N/D
+# module iir N=6 D=10
+
 # Uncomment if needed (single touch only)
 # module variance delta=30
 
diff --git a/plugins/Makefile.am b/plugins/Makefile.am
index 8c999ba..7c640b4 100644
--- a/plugins/Makefile.am
+++ b/plugins/Makefile.am
@@ -59,6 +59,12 @@ else
 SKIP_MODULE =
 endif
 
+if ENABLE_IIR_MODULE
+IIR_MODULE = iir.la
+else
+IIR_MODULE =
+endif
+
 if ENABLE_UCB1X00_MODULE
 UCB1X00_MODULE = ucb1x00.la
 else
@@ -152,6 +158,7 @@ pluginexec_LTLIBRARIES = \
 	$(PTHRES_MODULE) \
 	$(DEBOUNCE_MODULE) \
 	$(SKIP_MODULE) \
+	$(IIR_MODULE) \
 	$(UCB1X00_MODULE) \
 	$(CORGI_MODULE) \
 	$(COLLIE_MODULE) \
@@ -223,6 +230,13 @@ linear_h2200_la_LDFLAGS	+= -no-undefined
 endif
 linear_h2200_la_LIBADD	= $(top_builddir)/src/libts.la
 
+iir_la_SOURCES		= iir.c
+iir_la_LDFLAGS		= -module $(LTVSN)
+if WINDOWS
+iir_la_LDFLAGS		+= -no-undefined
+endif
+iir_la_LIBADD		= $(top_builddir)/src/libts.la
+
 # hw access
 corgi_la_SOURCES	= corgi-raw.c
 corgi_la_LDFLAGS	= -module $(LTVSN)
diff --git a/plugins/iir.c b/plugins/iir.c
new file mode 100644
index 0000000..af3ecd2
--- /dev/null
+++ b/plugins/iir.c
@@ -0,0 +1,267 @@
+/*
+ *  tslib/plugins/iir.c
+ *
+ *  Copyright (C) 2017 Martin Kepplinger
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ * Similar to dejitter, this is a smoothing filter to remove low-level noise.
+ * See https://en.wikipedia.org/wiki/Infinite_impulse_response for some theory.
+ * There is a trade-off between noise removal (smoothing) and responsiveness:
+ * The parameters N and D specify the level of smoothing in the form of a
+ * fraction (N/D).
+ *
+ * The discrete formula we use is found at http://dlbeer.co.nz/articles/tsf.html
+ */
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <stdint.h>
+
+#include <stdio.h>
+
+#include "config.h"
+#include "tslib.h"
+#include "tslib-filter.h"
+
+struct tslib_iir {
+	struct tslib_module_info module;
+	uint32_t D;
+	uint32_t N;
+	int32_t slots;
+	uint32_t nr;
+	int32_t s;
+	int32_t t;
+	uint8_t last_active; /* for finding pen-down */
+
+	int32_t *s_mt;
+	int32_t *t_mt;
+	uint8_t *last_active_mt; /* for finding pen-down */
+};
+
+/* legacy read interface */
+static int iir_read(struct tslib_module_info *info, struct ts_sample *samp,
+		    int nr)
+{
+	struct tslib_iir *iir = (struct tslib_iir *)info;
+	int32_t ret;
+	int32_t i;
+
+	ret = info->next->ops->read(info->next, samp, nr);
+	if (ret < 0)
+		return ret;
+
+	for (i = 0; i < ret; i++, samp++) {
+		if (samp->pressure == 0) { /* reset */
+			iir->s = samp->x;
+			iir->t = samp->y;
+			iir->last_active = 0;
+			continue;
+		} else if (iir->last_active == 0) {
+			iir->s = samp->x;
+			iir->t = samp->y;
+			iir->last_active = 1;
+			continue;
+		}
+
+		iir->s = (iir->N * iir->s + (iir->D - iir->N) * samp->x + iir->D / 2) / iir->D;
+	#ifdef DEBUG
+		printf("IIR: X: %d -> %d\n", samp->x, iir->s);
+	#endif
+		samp->x = iir->s;
+
+		iir->t = (iir->N * iir->t + (iir->D - iir->N) * samp->y + iir->D / 2) / iir->D;
+	#ifdef DEBUG
+		printf("IIR: Y: %d -> %d\n", samp->y, iir->t);
+	#endif
+		samp->y = iir->t;
+	}
+
+	return ret;
+}
+
+static int iir_read_mt(struct tslib_module_info *info,
+		       struct ts_sample_mt **samp, int max_slots, int nr)
+{
+	struct tslib_iir *iir = (struct tslib_iir *)info;
+	int32_t ret;
+	int32_t i, j;
+
+	if (!iir->s_mt || max_slots > iir->slots) {
+		if (iir->s_mt)
+			free(iir->s_mt);
+
+		if (iir->t_mt)
+			free(iir->t_mt);
+
+		if (iir->last_active_mt)
+			free(iir->last_active_mt);
+
+		iir->s_mt = calloc(max_slots, sizeof(int32_t));
+		if (!iir->s_mt)
+			return -ENOMEM;
+
+		iir->t_mt = calloc(max_slots, sizeof(int32_t));
+		if (!iir->t_mt)
+			return -ENOMEM;
+
+		iir->last_active_mt = calloc(max_slots, sizeof(uint8_t));
+		if (!iir->last_active_mt)
+			return -ENOMEM;
+
+		iir->slots = max_slots;
+	}
+
+	ret = info->next->ops->read_mt(info->next, samp, max_slots, nr);
+	if (ret < 0)
+		return ret;
+
+	for (i = 0; i < ret; i++) {
+		for (j = 0; j < max_slots; j++) {
+			if (samp[i][j].valid != 1)
+				continue;
+			/* reset */
+			if (samp[i][j].pressure == 0) { /* pen-up */
+				iir->s_mt[j] = samp[i][j].x;
+				iir->t_mt[j] = samp[i][j].y;
+				iir->last_active_mt[j] = 0;
+				continue;
+			} else if (iir->last_active_mt[j] == 0) { /* pen-down */
+				iir->s_mt[j] = samp[i][j].x;
+				iir->t_mt[j] = samp[i][j].y;
+				iir->last_active_mt[j] = 1;
+				continue;
+			}
+
+			iir->s_mt[j] = (iir->N * iir->s_mt[j] +
+					(iir->D - iir->N) * samp[i][j].x +
+					iir->D / 2)
+				       / iir->D;
+		#ifdef DEBUG
+			printf("IIR: (slot %d) X: %d -> %d\n",
+			       j, samp[i][j].x, iir->s_mt[j]);
+		#endif
+			samp[i][j].x = iir->s_mt[j];
+
+			iir->t_mt[j] = (iir->N * iir->t_mt[j] +
+					(iir->D - iir->N) * samp[i][j].y +
+					iir->D / 2)
+				       / iir->D;
+		#ifdef DEBUG
+			printf("IIR: (slot %d) Y: %d -> %d\n",
+			       j, samp[i][j].y, iir->t_mt[j]);
+		#endif
+			samp[i][j].y = iir->t_mt[j];
+		}
+	}
+
+	return ret;
+}
+
+static int iir_fini(struct tslib_module_info *info)
+{
+	struct tslib_iir *iir = (struct tslib_iir *)info;
+
+	if (iir->s_mt)
+		free(iir->s_mt);
+
+	if (iir->t_mt)
+		free(iir->t_mt);
+
+	free(info);
+
+	return 0;
+}
+
+static const struct tslib_ops iir_ops = {
+	.read		= iir_read,
+	.read_mt	= iir_read_mt,
+	.fini		= iir_fini,
+};
+
+static int iir_opt(struct tslib_module_info *inf, char *str, void *data)
+{
+	struct tslib_iir *iir = (struct tslib_iir *)inf;
+	unsigned long v;
+	int32_t err = errno;
+
+	v = strtoul(str, NULL, 0);
+
+	if (v == ULONG_MAX && errno == ERANGE)
+		return -1;
+
+	errno = err;
+	switch ((int)(intptr_t)data) {
+	case 1:
+		iir->N = v;
+		break;
+
+	case 2:
+		iir->D = v;
+		if (iir->D == 0) {
+			printf("IIR: avoid division by zero: D=1 set\n");
+			iir->D = 1;
+		}
+		break;
+
+	default:
+		return -1;
+	}
+
+
+	return 0;
+}
+
+static const struct tslib_vars iir_vars[] = {
+	{ "N",	(void *)1, iir_opt },
+	{ "D",	(void *)2, iir_opt },
+};
+
+#define NR_VARS (sizeof(iir_vars) / sizeof(iir_vars[0]))
+
+TSAPI struct tslib_module_info *iir_mod_init(__attribute__ ((unused)) struct tsdev *dev,
+					     const char *params)
+{
+	struct tslib_iir *iir;
+
+	iir = malloc(sizeof(struct tslib_iir));
+	if (iir == NULL)
+		return NULL;
+
+	memset(iir, 0, sizeof(struct tslib_iir));
+	iir->module.ops = &iir_ops;
+
+	iir->N = 0;
+	iir->D = 0;
+	iir->slots = 0;
+	iir->nr = 0;
+	iir->s = 0;
+	iir->t = 0;
+	iir->s_mt = NULL;
+	iir->t_mt = NULL;
+	iir->last_active = 0;
+
+	if (tslib_parse_vars(&iir->module, iir_vars, NR_VARS, params)) {
+		free(iir);
+		return NULL;
+	}
+
+	return &iir->module;
+}
+
+#ifndef TSLIB_STATIC_DEJITTER_MODULE
+	TSLIB_MODULE_INIT(iir_mod_init);
+#endif
diff --git a/plugins/plugins.h b/plugins/plugins.h
index 01d39a2..8f9f5d2 100644
--- a/plugins/plugins.h
+++ b/plugins/plugins.h
@@ -9,6 +9,7 @@ TSLIB_DECLARE_MODULE(median);
 TSLIB_DECLARE_MODULE(pthres);
 TSLIB_DECLARE_MODULE(debounce);
 TSLIB_DECLARE_MODULE(skip);
+TSLIB_DECLARE_MODULE(iir);
 
 TSLIB_DECLARE_MODULE(ucb1x00);
 TSLIB_DECLARE_MODULE(corgi);
diff --git a/src/Makefile.am b/src/Makefile.am
index 8aa80c4..ec0f539 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -86,6 +86,10 @@ if ENABLE_STATIC_SKIP_MODULE
 libts_la_SOURCES += $(top_srcdir)/plugins/skip.c
 endif
 
+if ENABLE_STATIC_IIR_MODULE
+libts_la_SOURCES += $(top_srcdir)/plugins/iir.c
+endif
+
 if ENABLE_STATIC_WAVESHARE_MODULE
 libts_la_SOURCES += $(top_srcdir)/plugins/waveshare-raw.c
 endif
diff --git a/src/ts_load_module.c b/src/ts_load_module.c
index a178d19..c0e1f39 100644
--- a/src/ts_load_module.c
+++ b/src/ts_load_module.c
@@ -77,6 +77,9 @@ static const struct {
 #ifdef TSLIB_STATIC_SKIP_MODULE
 	{ "skip", skip_mod_init },
 #endif
+#ifdef TSLIB_STATIC_IIR_MODULE
+	{ "iir", iir_mod_init },
+#endif
 };
 
 #define countof(arr) (sizeof(arr) / sizeof((arr)[0]))
-- 
2.1.4




[Index of Archives]     [AMD Graphics]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux