1 // Copyright 2013 The ChromiumOS Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include <map> 6 #include <gtest/gtest.h> // for FRIEND_TEST 7 8 #include "include/filter_interpreter.h" 9 #include "include/finger_metrics.h" 10 #include "include/gestures.h" 11 #include "include/macros.h" 12 #include "include/tracer.h" 13 14 #ifndef GESTURES_STATIONARY_WIGGLE_FILTER_INTERPRETER_H_ 15 #define GESTURES_STATIONARY_WIGGLE_FILTER_INTERPRETER_H_ 16 17 namespace gestures { 18 19 // This interpreter tries to detect if a finger is moving/stationary from 20 // 'pure' signal energy level of samples. When a finger is detected as 21 // stationary, the warp flags will be set to cease the cursor wiggling. 22 // 23 // As there is noise signal mixed in original data, cursor wiggling could be 24 // observed on all platforms. When a finger is stationary, we could observe 25 // the noise is mainly from a high frequency wiggling and a low-frequency drift 26 // from input data. Theoretically, from signal processing of input data, 27 // pure finger signal could be obtained from a bandpass filter to remove these 28 // two noise sources. To simplify the implementation, the patch tries to 29 // implement a bandpass filter with a series connection of a high-pass filter 30 // and a low-pass filter. 31 // 32 // +------------------+ +-------------------+ 33 // original --\ | high-pass filter | --\ | low-pass filter | --\ pure 34 // signal --/ |for low-freq drift| --/ |for high-freq noise| --/ signal 35 // +------------------+ +-------------------+ 36 // 37 // Let's say we have original signal samples, s0, s1... sn. The high-pass 38 // filter for removing low-frequency drift could be simply implemented 39 // by shifting original signal to average of samples, i.e. the output of the 40 // high-pass filter for a sample window of 5: 41 // 42 // m4 = s4 - (s0 + s1 + ... + s4) / 5 43 // m5 = s5 - (s1 + s2 + ... + s5) / 5 44 // ... 45 // 46 // Then, followed with a low-pass filter to remove high frequency noise. 47 // Since our goal is to detect if a finger is stationary or not, the simplest 48 // implementation of the low-pass filter is to get pure signal from the 49 // average of mixed signal. 50 // 51 // p4 = (m0 + m1 + ... + m4) / 5 52 // p5 = (m1 + m2 + ... + m5) / 5 53 // ... 54 // 55 // Once we have the 'pure' signal data, it is easy to detect if a finger is 56 // stationary or not with a reasonable threshold of signal energy of pure signal 57 // samples. 58 // 59 // signal energy: 60 // se4 = p0^2 + p1^2 ... + p4^2 61 // se5 = p1^2 + p2^2 ... + p5^2 62 // 63 64 #define SIGNAL_SAMPLES 5 // number of signal samples 65 66 struct FingerEnergy { 67 float x; // original position_x 68 float y; // original position_y 69 float mixed_x; // mixed signal of X 70 float mixed_y; // mixed signal of Y 71 float energy_x; // signal energy of X 72 float energy_y; // signal energy of Y 73 74 bool operator==(const FingerEnergy& that) const { 75 return x == that.x && 76 y == that.y && 77 mixed_x == that.mixed_x && 78 mixed_y == that.mixed_y && 79 energy_x == that.energy_x && 80 energy_y == that.energy_y; 81 } 82 bool operator!=(const FingerEnergy& that) const { return !(*this == that); } 83 }; 84 85 class FingerEnergyHistory { 86 public: FingerEnergyHistory()87 FingerEnergyHistory() 88 : size_(0), 89 head_(0), 90 moving_(false), 91 idle_time_(0.1), 92 prev_(0) {} 93 94 // Push the current finger data into the history buffer 95 void PushFingerState(const FingerState &fs, const stime_t timestamp); 96 97 // Get previous moving state moving()98 bool moving() const { return moving_; } 99 100 // Check if the buffer is filled up HasEnoughSamples()101 bool HasEnoughSamples() const { return (size_ == max_size_); } 102 103 // 0 is newest, 1 is next newest, ..., size_ - 1 is oldest. 104 const FingerEnergy& Get(size_t offset) const; 105 106 // Calculate current signal energy. A finger changes from stationary 107 // to moving if its signal energy is greater than threshold. 108 // If its previous state is moving, a hysteresis value could be used 109 // to check if current signal energy could sustain the moving state. 110 bool IsFingerMoving(float threshold); 111 112 bool operator==(const FingerEnergyHistory& that) const; 113 bool operator!=(const FingerEnergyHistory& that) const; 114 115 private: 116 FingerEnergy history_[SIGNAL_SAMPLES]; // the finger energy buffer 117 size_t max_size_ = arraysize(history_); 118 size_t size_; 119 size_t head_; 120 bool moving_; 121 stime_t idle_time_; // timeout for finger without state change 122 stime_t prev_; 123 }; 124 125 class StationaryWiggleFilterInterpreter : public FilterInterpreter { 126 FRIEND_TEST(StationaryWiggleFilterInterpreterTest, SimpleTest); 127 128 public: 129 // Takes ownership of |next|: 130 StationaryWiggleFilterInterpreter(PropRegistry* prop_reg, 131 Interpreter* next, 132 Tracer* tracer); ~StationaryWiggleFilterInterpreter()133 virtual ~StationaryWiggleFilterInterpreter() {} 134 135 protected: 136 virtual void SyncInterpretImpl(HardwareState* hwstate, stime_t* timeout); 137 138 private: 139 // Calculate signal energy from input data and update finger flag if 140 // a finger is stationary 141 void UpdateStationaryFlags(HardwareState* hwstate); 142 143 // Map of finger energy histories 144 typedef std::map<short, FingerEnergyHistory> FingerEnergyHistoryMap; 145 FingerEnergyHistoryMap histories_; 146 147 // True if this interpreter is effective 148 BoolProperty enabled_; 149 150 // Threshold and hysteresis of signal energy for finger moving/stationary 151 DoubleProperty threshold_; 152 DoubleProperty hysteresis_; 153 154 DISALLOW_COPY_AND_ASSIGN(StationaryWiggleFilterInterpreter); 155 }; 156 157 } // namespace gestures 158 159 #endif // GESTURES_STATIONARY_WIGGLE_FILTER_INTERPRETER_H_ 160