• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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