blob: a83c4d1610f9562384fb99885f2a3b00df55cc08 [file] [log] [blame]
// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "gestures/include/iir_filter_interpreter.h"
#include <utility>
namespace gestures {
void IirFilterInterpreter::IoHistory::Increment() {
out_head = NextOutHead();
in_head = NextInHead();
}
// Uses exact comparisions, rather than approximate float comparisions,
// for operator==
bool IirFilterInterpreter::IoHistory::operator==(
const IirFilterInterpreter::IoHistory& that) const {
for (size_t i = 0; i < kInSize; i++)
if (in[i] != that.in[i])
return false;
for (size_t i = 0; i < kOutSize; i++)
if (out[i] != that.out[i])
return false;
return true;
}
// The default filter is a low-pass 2nd order Butterworth IIR filter with a
// normalized cutoff frequency of 0.2.
IirFilterInterpreter::IirFilterInterpreter(PropRegistry* prop_reg,
Interpreter* next)
: b0_(prop_reg, "IIR b0", 0.0674552738890719, this),
b1_(prop_reg, "IIR b1", 0.134910547778144, this),
b2_(prop_reg, "IIR b2", 0.0674552738890719, this),
b3_(prop_reg, "IIR b3", 0.0, this),
a1_(prop_reg, "IIR a1", -1.1429805025399, this),
a2_(prop_reg, "IIR a2", 0.412801598096189, this),
iir_dist_thresh_(prop_reg, "IIR Distance Threshold", 10, this),
using_iir_(true),
is_semi_mt_device_(false) {
next_.reset(next);
}
Gesture* IirFilterInterpreter::SyncInterpret(HardwareState* hwstate,
stime_t* timeout) {
// Delete old entries from map
short dead_ids[histories_.size()];
size_t dead_ids_len = 0;
for (map<short, IoHistory, kMaxFingers>::iterator it = histories_.begin(),
e = histories_.end(); it != e; ++it)
if (!hwstate->GetFingerState((*it).first))
dead_ids[dead_ids_len++] = (*it).first;
for (size_t i = 0; i < dead_ids_len; ++i)
histories_.erase(dead_ids[i]);
// Modify current hwstate
for (size_t i = 0; i < hwstate->finger_cnt; i++) {
FingerState* fs = &hwstate->fingers[i];
map<short, IoHistory, kMaxFingers>::iterator history =
histories_.find(fs->tracking_id);
if (history == histories_.end()) {
// new finger
IoHistory hist(*fs);
histories_[fs->tracking_id] = hist;
continue;
}
// existing finger, apply filter
IoHistory* hist = &(*history).second;
float dx = fs->position_x - hist->PrevOut(0)->position_x;
float dy = fs->position_y - hist->PrevOut(0)->position_y;
// IIR filter is too smooth for a quick finger movement. We do a simple
// rolling average if the position change between current and previous
// frames is larger than iir_dist_thresh_.
if (dx * dx + dy * dy > iir_dist_thresh_.val_ * iir_dist_thresh_.val_)
using_iir_ = false;
else
using_iir_ = true;
// TODO(adlr): consider applying filter to other fields
float FingerState::*fields[] = { &FingerState::position_x,
&FingerState::position_y,
&FingerState::pressure };
for (size_t f_idx = 0; f_idx < arraysize(fields); f_idx++) {
float FingerState::*field = fields[f_idx];
// Keep the current pressure reading, so we could make sure the pressure
// values will be same if there is two fingers on a SemiMT device.
if (is_semi_mt_device_ && (field == &FingerState::pressure)) {
hist->NextOut()->pressure = fs->pressure;
continue;
}
if (using_iir_) {
hist->NextOut()->*field =
b3_.val_ * hist->PrevIn(2)->*field +
b2_.val_ * hist->PrevIn(1)->*field +
b1_.val_ * hist->PrevIn(0)->*field +
b0_.val_ * fs->*field -
a2_.val_ * hist->PrevOut(1)->*field -
a1_.val_ * hist->PrevOut(0)->*field;
} else {
hist->NextOut()->*field = 0.5 * (fs->*field + hist->PrevOut(0)->*field);
}
}
float FingerState::*pass_fields[] = { &FingerState::touch_major,
&FingerState::touch_minor,
&FingerState::width_major,
&FingerState::width_minor,
&FingerState::orientation };
for (size_t f_idx = 0; f_idx < arraysize(fields); f_idx++)
hist->NextOut()->*pass_fields[f_idx] = fs->*pass_fields[f_idx];
hist->NextOut()->flags = fs->flags;
*hist->NextIn() = *fs;
*fs = *hist->NextOut();
hist->Increment();
}
return next_->SyncInterpret(hwstate, timeout);
}
Gesture* IirFilterInterpreter::HandleTimer(stime_t now, stime_t* timeout) {
return next_->HandleTimer(now, timeout);
}
void IirFilterInterpreter::SetHardwareProperties(
const HardwareProperties& hwprops) {
is_semi_mt_device_ = hwprops.support_semi_mt;
return next_->SetHardwareProperties(hwprops);
}
void IirFilterInterpreter::DoubleWasWritten(DoubleProperty* prop) {
histories_.clear();
}
} // namespace gestures