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

Commit 4cb1e487 authored by Ytai Ben-Tsvi's avatar Ytai Ben-Tsvi
Browse files

Consider the screen still until proven otherwise

In order to avoid always forcing static mode after initialization or
recentering, consider the screen still by default.

This required adding a defaultValue option to the detector, to be used
during the initialization period.

Test: atest --host libheadtracking-test
Change-Id: I7f5a39a0fbca898c55f637e311ec1ea44a10d9f3
parent a7541bec
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -42,11 +42,13 @@ class HeadTrackingProcessorImpl : public HeadTrackingProcessor {
                  .rotationalDriftTimeConstant = options.rotationalDriftTimeConstant,
          }),
          mHeadStillnessDetector(StillnessDetector::Options{
                  .defaultValue = false,
                  .windowDuration = options.autoRecenterWindowDuration,
                  .translationalThreshold = options.autoRecenterTranslationalThreshold,
                  .rotationalThreshold = options.autoRecenterRotationalThreshold,
          }),
          mScreenStillnessDetector(StillnessDetector::Options{
                  .defaultValue = true,
                  .windowDuration = options.screenStillnessWindowDuration,
                  .translationalThreshold = options.screenStillnessTranslationalThreshold,
                  .rotationalThreshold = options.screenStillnessRotationalThreshold,
+54 −35
Original line number Diff line number Diff line
@@ -28,101 +28,120 @@ using Eigen::Quaternionf;
using Eigen::Vector3f;
using Options = StillnessDetector::Options;

TEST(StillnessDetectorTest, Still) {
    StillnessDetector detector(Options{
            .windowDuration = 1000, .translationalThreshold = 1, .rotationalThreshold = 0.05});
class StillnessDetectorTest : public testing::TestWithParam<bool> {
  public:
    void SetUp() override { mDefaultValue = GetParam(); }

  protected:
    bool mDefaultValue;
};

TEST_P(StillnessDetectorTest, Still) {
    StillnessDetector detector(Options{.defaultValue = mDefaultValue,
                                       .windowDuration = 1000,
                                       .translationalThreshold = 1,
                                       .rotationalThreshold = 0.05});

    const Pose3f baseline(Vector3f{1, 2, 3}, Quaternionf::UnitRandom());
    const Pose3f withinThreshold =
            baseline * Pose3f(Vector3f(0.3, -0.3, 0), rotateX(0.01) * rotateY(-0.01));

    EXPECT_FALSE(detector.calculate(0));
    EXPECT_EQ(mDefaultValue, detector.calculate(0));
    detector.setInput(0, baseline);
    EXPECT_FALSE(detector.calculate(0));
    EXPECT_EQ(mDefaultValue, detector.calculate(0));
    detector.setInput(300, withinThreshold);
    EXPECT_FALSE(detector.calculate(300));
    EXPECT_EQ(mDefaultValue, detector.calculate(300));
    detector.setInput(600, baseline);
    EXPECT_FALSE(detector.calculate(600));
    EXPECT_EQ(mDefaultValue, detector.calculate(600));
    detector.setInput(999, withinThreshold);
    EXPECT_FALSE(detector.calculate(999));
    EXPECT_EQ(mDefaultValue, detector.calculate(999));
    detector.setInput(1000, baseline);
    EXPECT_TRUE(detector.calculate(1000));
}

TEST(StillnessDetectorTest, ZeroDuration) {
    StillnessDetector detector(Options{.windowDuration = 0});
TEST_P(StillnessDetectorTest, ZeroDuration) {
    StillnessDetector detector(Options{.defaultValue = mDefaultValue, .windowDuration = 0});
    EXPECT_TRUE(detector.calculate(0));
    EXPECT_TRUE(detector.calculate(1000));
}

TEST(StillnessDetectorTest, NotStillTranslation) {
    StillnessDetector detector(Options{
            .windowDuration = 1000, .translationalThreshold = 1, .rotationalThreshold = 0.05});
TEST_P(StillnessDetectorTest, NotStillTranslation) {
    StillnessDetector detector(Options{.defaultValue = mDefaultValue,
                                       .windowDuration = 1000,
                                       .translationalThreshold = 1,
                                       .rotationalThreshold = 0.05});

    const Pose3f baseline(Vector3f{1, 2, 3}, Quaternionf::UnitRandom());
    const Pose3f withinThreshold =
            baseline * Pose3f(Vector3f(0.3, -0.3, 0), rotateX(0.01) * rotateY(-0.01));
    const Pose3f outsideThreshold = baseline * Pose3f(Vector3f(1, 1, 0));

    EXPECT_FALSE(detector.calculate(0));
    EXPECT_EQ(mDefaultValue, detector.calculate(0));
    detector.setInput(0, baseline);
    EXPECT_FALSE(detector.calculate(0));
    EXPECT_EQ(mDefaultValue, detector.calculate(0));
    detector.setInput(300, outsideThreshold);
    EXPECT_FALSE(detector.calculate(300));
    EXPECT_EQ(mDefaultValue, detector.calculate(300));
    detector.setInput(600, baseline);
    EXPECT_FALSE(detector.calculate(600));
    EXPECT_EQ(mDefaultValue, detector.calculate(600));
    detector.setInput(900, withinThreshold);
    EXPECT_FALSE(detector.calculate(900));
    EXPECT_EQ(mDefaultValue, detector.calculate(900));
    detector.setInput(1299, baseline);
    EXPECT_FALSE(detector.calculate(1299));
    EXPECT_TRUE(detector.calculate(1300));
}

TEST(StillnessDetectorTest, NotStillRotation) {
    StillnessDetector detector(Options{
            .windowDuration = 1000, .translationalThreshold = 1, .rotationalThreshold = 0.05});
TEST_P(StillnessDetectorTest, NotStillRotation) {
    StillnessDetector detector(Options{.defaultValue = mDefaultValue,
                                       .windowDuration = 1000,
                                       .translationalThreshold = 1,
                                       .rotationalThreshold = 0.05});

    const Pose3f baseline(Vector3f{1, 2, 3}, Quaternionf::UnitRandom());
    const Pose3f withinThreshold =
            baseline * Pose3f(Vector3f(0.3, -0.3, 0), rotateX(0.03) * rotateY(-0.03));
    const Pose3f outsideThreshold = baseline * Pose3f(rotateZ(0.06));
    EXPECT_FALSE(detector.calculate(0));
    EXPECT_EQ(mDefaultValue, detector.calculate(0));
    detector.setInput(0, baseline);
    EXPECT_FALSE(detector.calculate(0));
    EXPECT_EQ(mDefaultValue, detector.calculate(0));
    detector.setInput(300, outsideThreshold);
    EXPECT_FALSE(detector.calculate(300));
    EXPECT_EQ(mDefaultValue, detector.calculate(300));
    detector.setInput(600, baseline);
    EXPECT_FALSE(detector.calculate(600));
    EXPECT_EQ(mDefaultValue, detector.calculate(600));
    detector.setInput(900, withinThreshold);
    EXPECT_FALSE(detector.calculate(900));
    EXPECT_EQ(mDefaultValue, detector.calculate(900));
    detector.setInput(1299, baseline);
    EXPECT_FALSE(detector.calculate(1299));
    EXPECT_TRUE(detector.calculate(1300));
}

TEST(StillnessDetectorTest, Reset) {
    StillnessDetector detector(Options{
            .windowDuration = 1000, .translationalThreshold = 1, .rotationalThreshold = 0.05});
TEST_P(StillnessDetectorTest, Reset) {
    StillnessDetector detector(Options{.defaultValue = mDefaultValue,
                                       .windowDuration = 1000,
                                       .translationalThreshold = 1,
                                       .rotationalThreshold = 0.05});

    const Pose3f baseline(Vector3f{1, 2, 3}, Quaternionf::UnitRandom());
    const Pose3f withinThreshold =
            baseline * Pose3f(Vector3f(0.3, -0.3, 0), rotateX(0.01) * rotateY(-0.01));
    EXPECT_FALSE(detector.calculate(0));
    EXPECT_EQ(mDefaultValue, detector.calculate(0));
    detector.setInput(0, baseline);
    EXPECT_FALSE(detector.calculate(0));
    EXPECT_EQ(mDefaultValue, detector.calculate(0));
    detector.reset();
    detector.setInput(600, baseline);
    EXPECT_FALSE(detector.calculate(600));
    EXPECT_EQ(mDefaultValue, detector.calculate(600));
    detector.setInput(900, withinThreshold);
    EXPECT_FALSE(detector.calculate(900));
    EXPECT_EQ(mDefaultValue, detector.calculate(900));
    detector.setInput(1200, baseline);
    EXPECT_FALSE(detector.calculate(1200));
    EXPECT_EQ(mDefaultValue, detector.calculate(1200));
    detector.setInput(1599, withinThreshold);
    EXPECT_FALSE(detector.calculate(1599));
    EXPECT_EQ(mDefaultValue, detector.calculate(1599));
    detector.setInput(1600, baseline);
    EXPECT_TRUE(detector.calculate(1600));
}

INSTANTIATE_TEST_SUITE_P(StillnessDetectorTestParametrized, StillnessDetectorTest,
                         testing::Values(false, true));

}  // namespace
}  // namespace media
}  // namespace android
+4 −4
Original line number Diff line number Diff line
@@ -35,13 +35,13 @@ void StillnessDetector::setInput(int64_t timestamp, const Pose3f& input) {
bool StillnessDetector::calculate(int64_t timestamp) {
    discardOld(timestamp);

    // If the window has not been full, we don't consider ourselves still.
    // If the window has not been full, return the default value.
    if (!mWindowFull) {
        return false;
        return mOptions.defaultValue;
    }

    // An empty FIFO and window full is considered still (this will happen in the unlikely case when
    // the window duration is shorter than the gap between samples).
    // An empty FIFO and window full is considered still (this will happen when the window duration
    // is shorter than the gap between samples, including the window size being 0).
    if (mFifo.empty()) {
        return true;
    }
+9 −4
Original line number Diff line number Diff line
@@ -36,10 +36,11 @@ namespace media {
 *    bool still = detector.calculate(timestamp);
 * }
 *
 * The stream is considered not still until a sufficient number of samples has been provided for an
 * initial fill-up of the window. In the special case of the window size being 0, this is not
 * required and the state is considered always "still". The reset() method can be used to empty the
 * window again and get back to this initial state.
 * The detection is not considered reliable until a sufficient number of samples has been provided
 * for an initial fill-up of the window. During that time, the detector will return whatever default
 * value has been configured.
 * The reset() method can be used to empty the window again and get back to this initial state.
 * In the special case of the window size being 0, the state will always be considered "still".
 */
class StillnessDetector {
  public:
@@ -47,6 +48,10 @@ class StillnessDetector {
     * Configuration options for the detector.
     */
    struct Options {
        /**
         * During the initial fill of the window, should we consider the state still?
         */
         bool defaultValue;
        /**
         * How long is the window, in ticks. The special value of 0 indicates that the stream is
         * always considered still.