aboutsummaryrefslogtreecommitdiff
path: root/drivers/input
diff options
context:
space:
mode:
authormerge <null@invalid>2008-12-08 11:03:03 +0000
committerAndy Green <agreen@pads.home.warmcat.com>2008-12-08 11:03:03 +0000
commitc3be736aeb24f44cf95dfad8fe3f1c7c6f367aaf (patch)
tree1ba8dbf5784f805847652d896d9ee98ceffae22b /drivers/input
parent2eb3742db1ecd3d9ffe8f02eaa8def1b2bd2a2c2 (diff)
MERGE-via-pending-tracking-hist-MERGE-via-stable-tracking-remove-skip-filter-1228733704
pending-tracking-hist top was MERGE-via-stable-tracking-remove-skip-filter-1228733704 / 552c6fdd4c644ab2618ad27564d159ed28bbd859 ... parent commitmessage: From: merge <null@invalid> MERGE-via-stable-tracking-hist-remove-skip-filter stable-tracking-hist top was remove-skip-filter / 92bdef8636873a19efc05b2a19578a0aa93dba41 ... parent commitmessage: From: Nelson Castillo <nelsoneci@gmail.com> Remove skip filter With more reliable points median and mean filters perform a better job. We no longer need this filter. Signed-off-by: Nelson Castillo <nelsoneci@gmail.com>
Diffstat (limited to 'drivers/input')
-rw-r--r--drivers/input/touchscreen/Kconfig8
-rw-r--r--drivers/input/touchscreen/Makefile2
-rw-r--r--drivers/input/touchscreen/s3c2410_ts.c119
-rw-r--r--drivers/input/touchscreen/ts_filter_group.c217
-rw-r--r--drivers/input/touchscreen/ts_filter_variance.c205
5 files changed, 233 insertions, 318 deletions
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 98566fd3342..a0f8599a5b2 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -21,13 +21,13 @@ menuconfig TOUCHSCREEN_FILTER
if TOUCHSCREEN_FILTER
-config TOUCHSCREEN_FILTER_VARIANCE
- bool "Variance Touchscreen Filter"
+config TOUCHSCREEN_FILTER_GROUP
+ bool "Group 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.
+ Say Y here if you want to use the Group touchscreen filter, it
+ avoids using atypical samples.
config TOUCHSCREEN_FILTER_MEDIAN
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index ee6bd283668..9a6162cecc5 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -33,6 +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_GROUP) += ts_filter_group.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 ea0f58aaf32..8e6bc0a0371 100644
--- a/drivers/input/touchscreen/s3c2410_ts.c
+++ b/drivers/input/touchscreen/s3c2410_ts.c
@@ -19,7 +19,7 @@
* ChangeLog
*
* 2004-09-05: Herbert Pƶtzl <herbert@13thfloor.at>
- * - added clock (de-)allocation code
+ * - added clock (de-)allocation code
*
* 2005-03-06: Arnaud Patard <arnaud.patard@rtp-net.org>
* - h1940_ -> s3c2410 (this driver is now also used on the n30
@@ -35,11 +35,14 @@
* controller
*
* 2007-05-23: Harald Welte <laforge@openmoko.org>
- * - Add proper support for S32440
+ * - Add proper support for S32440
*
* 2008-06-23: Andy Green <andy@openmoko.com>
* - removed averaging system
* - added generic Touchscreen filter stuff
+ *
+ * 2008-11-27: Nelson Castillo <arhuaco@freaks-unidos.net>
+ * - improve interrupt handling
*/
#include <linux/errno.h>
@@ -97,13 +100,9 @@ static char *s3c2410ts_name = "s3c2410 TouchScreen";
#define TS_EVENT_FIFO_SIZE (2 << 6) /* must be a power of 2 */
#define TS_STATE_STANDBY 0 /* initial state */
-#define TS_STATE_PRESSED_PENDING 1
-#define TS_STATE_PRESSED 2
-#define TS_STATE_RELEASE_PENDING 3
-#define TS_STATE_RELEASE 4
-
-#define SKIP_NHEAD 2
-#define SKIP_NTAIL 1
+#define TS_STATE_PRESSED 1
+#define TS_STATE_RELEASE_PENDING 2
+#define TS_STATE_RELEASE 3
/*
* Per-touchscreen data.
@@ -180,92 +179,8 @@ static void ts_input_report(int event, int coords[])
input_sync(ts.dev);
}
-
-/*
- * Skip filter for touchscreen values.
- *
- * Problem: The first and the last sample might be unreliable. We provide
- * this filter as a separate function in order to keep the event_send_timer_f
- * function simple. This filter:
- *
- * - Skips NHEAD points after IE_DOWN
- * - Skips NTAIL points before IE_UP
- * - Ignores a click if we have less than (NHEAD + NTAILl + 1) points
- */
-
-struct skip_filter_event {
- int coords[2];
-};
-
-struct skip_filter {
- unsigned N;
- unsigned M;
- int sent;
- struct skip_filter_event buf[SKIP_NTAIL];
-};
-
-struct skip_filter ts_skip;
-
-static void ts_skip_filter_reset(void)
-{
- ts_skip.N = 0;
- ts_skip.M = 0;
- ts_skip.sent = 0;
-}
-
-static void ts_skip_filter(int event, int coords[])
-{
- /* skip the first N samples */
- if (ts_skip.N < SKIP_NHEAD) {
- if (IE_UP == event)
- ts_skip_filter_reset();
- else
- ts_skip.N++;
- return;
- }
-
- /* We didn't send DOWN -- Ignore UP */
- if (IE_UP == event && !ts_skip.sent) {
- ts_skip_filter_reset();
- return;
- }
-
- /* Just accept the event if NTAIL == 0 */
- if (!SKIP_NTAIL) {
- ts_input_report(event, coords);
- if (IE_UP == event)
- ts_skip_filter_reset();
- else
- ts_skip.sent = 1;
- return;
- }
-
- /* NTAIL > 0, Queue current point if we need to */
- if (!ts_skip.sent && ts_skip.M < SKIP_NTAIL) {
- memcpy(&ts_skip.buf[ts_skip.M++].coords[0], &coords[0],
- sizeof(int) * 2);
- return;
- }
-
- /* queue full: accept one, queue one */
-
- if (ts_skip.M >= SKIP_NTAIL)
- ts_skip.M = 0;
-
- ts_input_report(event, ts_skip.buf[ts_skip.M].coords);
-
- if (event == IE_UP) {
- ts_skip_filter_reset();
- } else {
- memcpy(&ts_skip.buf[ts_skip.M++].coords[0], &coords[0],
- sizeof(int) * 2);
- ts_skip.sent = 1;
- }
-}
-
-
/*
- * Manage the state of the touchscreen. Send events to the skip filter.
+ * Manage the state of the touchscreen.
*/
static void event_send_timer_f(unsigned long data);
@@ -294,9 +209,6 @@ static void event_send_timer_f(unsigned long data)
if (ts.state == TS_STATE_RELEASE_PENDING)
/* Ignore short UP event */
ts.state = TS_STATE_PRESSED;
- else
- /* Defer PRESSED until we get a valid point */
- ts.state = TS_STATE_PRESSED_PENDING;
break;
case 'U':
@@ -313,9 +225,8 @@ static void event_send_timer_f(unsigned long data)
!= sizeof(int) * 2))
goto ts_exit_error;
- ts_skip_filter(IE_DOWN, buf);
+ ts_input_report(IE_DOWN, buf);
ts.state = TS_STATE_PRESSED;
-
break;
default:
@@ -332,7 +243,7 @@ static void event_send_timer_f(unsigned long data)
* while to avoid jitter. If we get a DOWN
* event we do not send it. */
- ts_skip_filter(IE_UP, NULL);
+ ts_input_report(IE_UP, NULL);
ts.state = TS_STATE_STANDBY;
if (ts.tsf[0])
@@ -517,8 +428,6 @@ static int __init s3c2410ts_probe(struct platform_device *pdev)
ts.dev->id.version = S3C2410TSVERSION;
ts.state = TS_STATE_STANDBY;
- ts_skip_filter_reset();
-
/* create the filter chain set up for the 2 coordinates we produce */
ret = ts_filter_create_chain(
(struct ts_filter_api **)&info->filter_sequence,
@@ -710,9 +619,3 @@ static void __exit s3c2410ts_exit(void)
module_init(s3c2410ts_init);
module_exit(s3c2410ts_exit);
-/*
- Local variables:
- compile-command: "make ARCH=arm CROSS_COMPILE=/usr/local/arm/3.3.2/bin/arm-linux- -k -C ../../.."
- c-basic-offset: 8
- End:
-*/
diff --git a/drivers/input/touchscreen/ts_filter_group.c b/drivers/input/touchscreen/ts_filter_group.c
new file mode 100644
index 00000000000..250613f9345
--- /dev/null
+++ b/drivers/input/touchscreen/ts_filter_group.c
@@ -0,0 +1,217 @@
+/*
+ * 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@freaks-unidos.net>
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * 1) We collect S samples.
+ *
+ * 2) For each dimension:
+ *
+ * - We sort the points.
+ * - 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.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/sort.h>
+#include <linux/ts_filter_group.h>
+
+static void ts_filter_group_clear_internal(struct ts_filter_group *tsfg,
+ int attempts)
+{
+ tsfg->N = 0;
+ tsfg->tries_left = attempts;
+}
+
+static void ts_filter_group_clear(struct ts_filter *tsf)
+{
+ struct ts_filter_group *tsfg = (struct ts_filter_group *)tsf;
+
+ ts_filter_group_clear_internal(tsfg, tsfg->config->attempts);
+
+ if (tsf->next) /* chain */
+ (tsf->next->api->clear)(tsf->next);
+}
+
+static struct ts_filter *ts_filter_group_create(void *conf, int count_coords)
+{
+ struct ts_filter_group *tsfg;
+ int i;
+
+ BUG_ON((count_coords < 1) || (count_coords > MAX_TS_FILTER_COORDS));
+
+ tsfg = kzalloc(sizeof(struct ts_filter_group), GFP_KERNEL);
+ if (!tsfg)
+ return NULL;
+
+ tsfg->config = (struct ts_filter_group_configuration *)conf;
+ tsfg->tsf.count_coords = count_coords;
+
+ BUG_ON(tsfg->config->attempts <= 0);
+
+ tsfg->samples[0] = kmalloc((2 + count_coords) * sizeof(int) *
+ tsfg->config->extent, GFP_KERNEL);
+ if (!tsfg->samples[0]) {
+ kfree(tsfg);
+ return NULL;
+ }
+ for (i = 1; i < count_coords; ++i)
+ tsfg->samples[i] = tsfg->samples[0] + i * tsfg->config->extent;
+ tsfg->sorted_samples = tsfg->samples[0] + count_coords *
+ tsfg->config->extent;
+ tsfg->group_size = tsfg->samples[0] + (1 + count_coords) *
+ tsfg->config->extent;
+
+ ts_filter_group_clear_internal(tsfg, tsfg->config->attempts);
+
+ printk(KERN_INFO" Created group ts filter len %d depth %d close %d "
+ "thresh %d\n", tsfg->config->extent, count_coords,
+ tsfg->config->close_enough, tsfg->config->threshold);
+
+ return &tsfg->tsf;
+}
+
+static void ts_filter_group_destroy(struct ts_filter *tsf)
+{
+ struct ts_filter_group *tsfg = (struct ts_filter_group *)tsf;
+
+ kfree(tsfg->samples[0]); /* first guy has pointer from kmalloc */
+ kfree(tsf);
+}
+
+static void ts_filter_group_scale(struct ts_filter *tsf, int *coords)
+{
+ if (tsf->next)
+ (tsf->next->api->scale)(tsf->next, coords);
+}
+
+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;
+}
+
+static int ts_filter_group_process(struct ts_filter *tsf, int *coords)
+{
+ struct ts_filter_group *tsfg = (struct ts_filter_group *)tsf;
+ int n;
+ int i;
+ int ret = 0; /* ask for more samples by default */
+
+ BUG_ON(tsfg->N >= tsfg->config->extent);
+
+ for (n = 0; n < tsf->count_coords; n++)
+ tsfg->samples[n][tsfg->N] = coords[n];
+
+ if (++tsfg->N < tsfg->config->extent)
+ return 0; /* we meed more samples */
+
+ 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));
+ 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;
+ }
+ 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 (best_size < tsfg->config->threshold) {
+ /* this set is not good enough for us */
+ if (--tsfg->tries_left) {
+ ts_filter_group_clear_internal
+ (tsfg, tsfg->tries_left);
+ return 0; /* ask for more samples */
+ }
+ return -1; /* we give up */
+ }
+
+ tsfg->range_min[n] = v[best_idx];
+ tsfg->range_max[n] = v[best_idx + best_size - 1];
+ }
+
+ BUG_ON(!tsf->next);
+
+ for (i = 0; i < tsfg->N; ++i) {
+ int r;
+
+ for (n = 0; n < tsfg->tsf.count_coords; ++n) {
+ coords[n] = tsfg->samples[n][i];
+ if (coords[n] < tsfg->range_min[n] ||
+ coords[n] > tsfg->range_max[n])
+ break;
+ }
+
+ if (n != tsfg->tsf.count_coords) /* sample not OK */
+ continue;
+
+ r = (tsf->next->api->process)(tsf->next, coords);
+ if (r) {
+ ret = r;
+ break;
+ }
+ }
+
+ ts_filter_group_clear_internal(tsfg, tsfg->config->attempts);
+
+ return ret;
+}
+
+struct ts_filter_api ts_filter_group_api = {
+ .create = ts_filter_group_create,
+ .destroy = ts_filter_group_destroy,
+ .clear = ts_filter_group_clear,
+ .process = ts_filter_group_process,
+ .scale = ts_filter_group_scale,
+};
+
diff --git a/drivers/input/touchscreen/ts_filter_variance.c b/drivers/input/touchscreen/ts_filter_variance.c
deleted file mode 100644
index c3352105c9f..00000000000
--- a/drivers/input/touchscreen/ts_filter_variance.c
+++ /dev/null
@@ -1,205 +0,0 @@
-/*
- * 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@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,
-};
-