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 "include/trend_classifying_filter_interpreter.h"
6
7 #include <cmath>
8
9 #include "include/filter_interpreter.h"
10 #include "include/finger_metrics.h"
11 #include "include/gestures.h"
12 #include "include/logging.h"
13 #include "include/prop_registry.h"
14 #include "include/tracer.h"
15 #include "include/util.h"
16
17 namespace {
18
19 // Constants for multiplication. Used in
20 // TrendClassifyingFilterInterpreter::ComputeKTVariance (see the header file).
21 const double k1_18 = 1.0 / 18.0;
22 const double k2_3 = 2.0 / 3.0;
23
24 const int kNumOfSamples = 20;
25
26 } // namespace {}
27
28 namespace gestures {
29
TrendClassifyingFilterInterpreter(PropRegistry * prop_reg,Interpreter * next,Tracer * tracer)30 TrendClassifyingFilterInterpreter::TrendClassifyingFilterInterpreter(
31 PropRegistry* prop_reg, Interpreter* next, Tracer* tracer)
32 : FilterInterpreter(NULL, next, tracer, false),
33 trend_classifying_filter_enable_(
34 prop_reg, "Trend Classifying Filter Enabled", true),
35 second_order_enable_(
36 prop_reg, "Trend Classifying 2nd-order Motion Enabled", false),
37 min_num_of_samples_(
38 prop_reg, "Trend Classifying Min Num of Samples", 6),
39 num_of_samples_(
40 prop_reg, "Trend Classifying Num of Samples", kNumOfSamples),
41 z_threshold_(
42 prop_reg, "Trend Classifying Z Threshold", 2.5758293035489004) {
43 InitName();
44 }
45
SyncInterpretImpl(HardwareState * hwstate,stime_t * timeout)46 void TrendClassifyingFilterInterpreter::SyncInterpretImpl(
47 HardwareState* hwstate, stime_t* timeout) {
48 if (trend_classifying_filter_enable_.val_)
49 UpdateFingerState(*hwstate);
50 next_->SyncInterpret(hwstate, timeout);
51 }
52
ComputeKTVariance(const int tie_n2,const int tie_n3,const size_t n_samples)53 double TrendClassifyingFilterInterpreter::ComputeKTVariance(const int tie_n2,
54 const int tie_n3, const size_t n_samples) {
55 // Replace divisions with multiplications for better performance
56 double var_n = n_samples * (n_samples - 1) * (2 * n_samples + 5) * k1_18;
57 double var_t = k2_3 * tie_n3 + tie_n2;
58 return var_n - var_t;
59 }
60
InterpretTestResult(const TrendType trend_type,const unsigned flag_increasing,const unsigned flag_decreasing,unsigned * flags)61 void TrendClassifyingFilterInterpreter::InterpretTestResult(
62 const TrendType trend_type,
63 const unsigned flag_increasing,
64 const unsigned flag_decreasing,
65 unsigned* flags) {
66 if (trend_type == TREND_INCREASING)
67 *flags |= flag_increasing;
68 else if (trend_type == TREND_DECREASING)
69 *flags |= flag_decreasing;
70 }
71
AddNewStateToBuffer(FingerHistory & history,const FingerState & fs)72 void TrendClassifyingFilterInterpreter::AddNewStateToBuffer(
73 FingerHistory& history, const FingerState& fs) {
74 // The history buffer is already full, pop one
75 if (history.size() == static_cast<size_t>(num_of_samples_.val_))
76 history.pop_front();
77
78 // Push the new finger state to the back of buffer
79 auto& current = history.emplace_back(fs);
80 if (history.size() == 1)
81 return;
82 auto& previous_end = history.at(-2);
83
84 current.DxAxis()->val = current.XAxis()->val - previous_end.XAxis()->val;
85 current.DyAxis()->val = current.YAxis()->val - previous_end.YAxis()->val;
86 // Update the nodes already in the buffer and compute the Kendall score/
87 // variance along the way. Complexity is O(|buffer|) per finger.
88 int tie_n2[KState::n_axes_] = { 0, 0, 0, 0, 0, 0 };
89 int tie_n3[KState::n_axes_] = { 0, 0, 0, 0, 0, 0 };
90 for (auto it = history.begin(); it != history.end(); ++it)
91 for (size_t i = 0; i < KState::n_axes_; i++)
92 if (it != history.begin() || !KState::IsDelta(i)) {
93 UpdateKTValuePair(&it->axes_[i], ¤t.axes_[i],
94 &tie_n2[i], &tie_n3[i]);
95 }
96 size_t n_samples = history.size();
97 for (size_t i = 0; i < KState::n_axes_; i++) {
98 current.axes_[i].var = ComputeKTVariance(tie_n2[i], tie_n3[i],
99 KState::IsDelta(i) ? n_samples - 1 : n_samples);
100 }
101 }
102
103 TrendClassifyingFilterInterpreter::TrendType
RunKTTest(const KState::KAxis * current,const size_t n_samples)104 TrendClassifyingFilterInterpreter::RunKTTest(const KState::KAxis* current,
105 const size_t n_samples) {
106 // Sample size is too small for a meaningful result
107 if (n_samples < static_cast<size_t>(min_num_of_samples_.val_))
108 return TREND_NONE;
109
110 // A zero score implies purely random behavior. Need to special-case it
111 // because the test might be fooled with a zero variance (e.g. all
112 // observations are tied).
113 if (!current->score)
114 return TREND_NONE;
115
116 // The test conduct the hypothesis test based on the fact that S/sqrt(Var(S))
117 // approximately follows the normal distribution. To optimize for speed,
118 // we reformulate the expression to drop the sqrt and division operations.
119 if (current->score * current->score <
120 z_threshold_.val_ * z_threshold_.val_ * current->var) {
121 return TREND_NONE;
122 }
123 return (current->score > 0) ? TREND_INCREASING : TREND_DECREASING;
124 }
125
UpdateFingerState(const HardwareState & hwstate)126 void TrendClassifyingFilterInterpreter::UpdateFingerState(
127 const HardwareState& hwstate) {
128 RemoveMissingIdsFromMap(&histories_, hwstate);
129
130 FingerState* fs = hwstate.fingers;
131 for (short i = 0; i < hwstate.finger_cnt; i++) {
132 // Update the map if the contact is new
133 if (!MapContainsKey(histories_, fs[i].tracking_id)) {
134 histories_[fs[i].tracking_id] = FingerHistory{};
135 }
136 auto& history = histories_[fs[i].tracking_id];
137
138 // Check if the score demonstrates statistical significance
139 AddNewStateToBuffer(history, fs[i]);
140 const auto& current = history.back();
141 const size_t n_samples = history.size();
142 for (size_t idx = 0; idx < KState::n_axes_; idx++)
143 if (second_order_enable_.val_ || !KState::IsDelta(idx)) {
144 TrendType result = RunKTTest(¤t.axes_[idx],
145 KState::IsDelta(idx) ? n_samples - 1 : n_samples);
146 InterpretTestResult(result, KState::IncFlag(idx),
147 KState::DecFlag(idx), &(fs[i].flags));
148 }
149 }
150 }
151
Init()152 void TrendClassifyingFilterInterpreter::KState::Init() {
153 for (size_t i = 0; i < KState::n_axes_; i++)
154 axes_[i].Init();
155 }
156
Init(const FingerState & fs)157 void TrendClassifyingFilterInterpreter::KState::Init(const FingerState& fs) {
158 Init();
159 XAxis()->val = fs.position_x, YAxis()->val = fs.position_y;
160 PressureAxis()->val = fs.pressure;
161 TouchMajorAxis()->val = fs.touch_major;
162 }
163
164 }
165