Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 53463397 authored by Paul Ramirez's avatar Paul Ramirez
Browse files

Fix One Euro filter's units of computation

Changed the units that the One Euro filter uses to compute the filtered
coordinates. This was causing a crash because if two timestamps were
sufficiently close to each other, by the time of implicitly converting
from nanoseconds to seconds, they were considered equal. This led to a
zero division when calculating the sampling frequency. Now, everything
is handled in the scale of nanoseconds, and conversion are done if and
only if they're necessary.

Bug: 297226446
Flag: EXEMPT bugfix
Test: TEST=libinput_tests; m $TEST && $ANDROID_HOST_OUT/nativetest64/$TEST/$TEST
Change-Id: I7fced6db447074cccb3d938eb9dc7a9707433f53
parent 08ee1999
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -44,7 +44,7 @@ public:
     * the previous call.
     * @param coords Coordinates to be overwritten by the corresponding filtered coordinates.
     */
    void filter(std::chrono::duration<float> timestamp, PointerCoords& coords);
    void filter(std::chrono::nanoseconds timestamp, PointerCoords& coords);

private:
    OneEuroFilter mXFilter;
+5 −5
Original line number Diff line number Diff line
@@ -56,7 +56,7 @@ public:
     * provided in the previous call.
     * @param rawPosition Position to be filtered.
     */
    float filter(std::chrono::duration<float> timestamp, float rawPosition);
    float filter(std::chrono::nanoseconds timestamp, float rawPosition);

private:
    /**
@@ -67,7 +67,7 @@ private:

    /**
     * Slope of the cutoff frequency criterion. This is the term scaling the absolute value of the
     * filtered signal's speed. The data member is dimensionless, that is, it does not have units.
     * filtered signal's speed. Units are 1 / position.
     */
    const float mBeta;

@@ -78,9 +78,9 @@ private:
    const float mSpeedCutoffFreq;

    /**
     * The timestamp from the previous call. Units are seconds.
     * The timestamp from the previous call.
     */
    std::optional<std::chrono::duration<float>> mPrevTimestamp;
    std::optional<std::chrono::nanoseconds> mPrevTimestamp;

    /**
     * The raw position from the previous call.
@@ -88,7 +88,7 @@ private:
    std::optional<float> mPrevRawPosition;

    /**
     * The filtered velocity from the previous call. Units are position per second.
     * The filtered velocity from the previous call. Units are position per nanosecond.
     */
    std::optional<float> mPrevFilteredVelocity;

+1 −1
Original line number Diff line number Diff line
@@ -23,7 +23,7 @@ namespace android {
CoordinateFilter::CoordinateFilter(float minCutoffFreq, float beta)
      : mXFilter{minCutoffFreq, beta}, mYFilter{minCutoffFreq, beta} {}

void CoordinateFilter::filter(std::chrono::duration<float> timestamp, PointerCoords& coords) {
void CoordinateFilter::filter(std::chrono::nanoseconds timestamp, PointerCoords& coords) {
    coords.setAxisValue(AMOTION_EVENT_AXIS_X, mXFilter.filter(timestamp, coords.getX()));
    coords.setAxisValue(AMOTION_EVENT_AXIS_Y, mYFilter.filter(timestamp, coords.getY()));
}
+21 −13
Original line number Diff line number Diff line
@@ -25,16 +25,24 @@
namespace android {
namespace {

using namespace std::literals::chrono_literals;

const float kHertzPerGigahertz = 1E9f;
const float kGigahertzPerHertz = 1E-9f;

// filteredSpeed's units are position per nanosecond. beta's units are 1 / position.
inline float cutoffFreq(float minCutoffFreq, float beta, float filteredSpeed) {
    return minCutoffFreq + beta * std::abs(filteredSpeed);
    return kHertzPerGigahertz *
            ((minCutoffFreq * kGigahertzPerHertz) + beta * std::abs(filteredSpeed));
}

inline float smoothingFactor(std::chrono::duration<float> samplingPeriod, float cutoffFreq) {
    return samplingPeriod.count() / (samplingPeriod.count() + (1.0 / (2.0 * M_PI * cutoffFreq)));
inline float smoothingFactor(std::chrono::nanoseconds samplingPeriod, float cutoffFreq) {
    const float constant = 2.0f * M_PI * samplingPeriod.count() * (cutoffFreq * kGigahertzPerHertz);
    return constant / (constant + 1);
}

inline float lowPassFilter(float rawPosition, float prevFilteredPosition, float smoothingFactor) {
    return smoothingFactor * rawPosition + (1 - smoothingFactor) * prevFilteredPosition;
inline float lowPassFilter(float rawValue, float prevFilteredValue, float smoothingFactor) {
    return smoothingFactor * rawValue + (1 - smoothingFactor) * prevFilteredValue;
}

} // namespace
@@ -42,17 +50,17 @@ inline float lowPassFilter(float rawPosition, float prevFilteredPosition, float
OneEuroFilter::OneEuroFilter(float minCutoffFreq, float beta, float speedCutoffFreq)
      : mMinCutoffFreq{minCutoffFreq}, mBeta{beta}, mSpeedCutoffFreq{speedCutoffFreq} {}

float OneEuroFilter::filter(std::chrono::duration<float> timestamp, float rawPosition) {
    LOG_IF(FATAL, mPrevFilteredPosition.has_value() && (timestamp <= *mPrevTimestamp))
            << "Timestamp must be greater than mPrevTimestamp";
float OneEuroFilter::filter(std::chrono::nanoseconds timestamp, float rawPosition) {
    LOG_IF(FATAL, mPrevTimestamp.has_value() && (*mPrevTimestamp >= timestamp))
            << "Timestamp must be greater than mPrevTimestamp. Timestamp: " << timestamp.count()
            << "ns. mPrevTimestamp: " << mPrevTimestamp->count() << "ns";

    const std::chrono::duration<float> samplingPeriod = (mPrevTimestamp.has_value())
            ? (timestamp - *mPrevTimestamp)
            : std::chrono::duration<float>{1.0};
    const std::chrono::nanoseconds samplingPeriod =
            (mPrevTimestamp.has_value()) ? (timestamp - *mPrevTimestamp) : 1s;

    const float rawVelocity = (mPrevFilteredPosition.has_value())
            ? ((rawPosition - *mPrevFilteredPosition) / samplingPeriod.count())
            : 0.0;
            ? ((rawPosition - *mPrevFilteredPosition) / (samplingPeriod.count()))
            : 0.0f;

    const float speedSmoothingFactor = smoothingFactor(samplingPeriod, mSpeedCutoffFreq);

+1 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@ cc_test {
        "IdGenerator_test.cpp",
        "InputChannel_test.cpp",
        "InputConsumer_test.cpp",
        "InputConsumerFilteredResampling_test.cpp",
        "InputConsumerResampling_test.cpp",
        "InputDevice_test.cpp",
        "InputEvent_test.cpp",
Loading