blob: f0d7f7ce89d3ec827d14f94aa61798299a3af021 [file] [log] [blame]
Ytai Ben-Tsvid83c42d2021-08-25 14:19:34 -07001/*
2 * Copyright 2021, The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16#include "SpatializerPoseController.h"
17
18#define LOG_TAG "VirtualizerStageController"
19//#define LOG_NDEBUG 0
20#include <utils/Log.h>
21#include <utils/SystemClock.h>
22
23namespace android {
24
25using media::createHeadTrackingProcessor;
26using media::HeadTrackingMode;
27using media::HeadTrackingProcessor;
28using media::Pose3f;
29using media::SensorPoseProvider;
30using media::Twist3f;
31
32using namespace std::chrono_literals;
33
34namespace {
35
36// This is how fast, in m/s, we allow position to shift during rate-limiting.
37constexpr auto kMaxTranslationalVelocity = 2 ;
38
39// This is how fast, in rad/s, we allow rotation angle to shift during rate-limiting.
40constexpr auto kMaxRotationalVelocity = 4 * M_PI ;
41
42// This should be set to the typical time scale that the translation sensors used drift in. This
43// means, loosely, for how long we can trust the reading to be "accurate enough". This would
44// determine the time constants used for high-pass filtering those readings. If the value is set
45// too high, we may experience drift. If it is set too low, we may experience poses tending toward
46// identity too fast.
47constexpr auto kTranslationalDriftTimeConstant = 20s;
48
49// This should be set to the typical time scale that the rotation sensors used drift in. This
50// means, loosely, for how long we can trust the reading to be "accurate enough". This would
51// determine the time constants used for high-pass filtering those readings. If the value is set
52// too high, we may experience drift. If it is set too low, we may experience poses tending toward
53// identity too fast.
54constexpr auto kRotationalDriftTimeConstant = 20s;
55
56// This is how far into the future we predict the head pose, using linear extrapolation based on
57// twist (velocity). It should be set to a value that matches the characteristic durations of moving
58// one's head. The higher we set this, the more latency we are able to reduce, but setting this too
59// high will result in high prediction errors whenever the head accelerates (changes velocity).
60constexpr auto kPredictionDuration = 10ms;
61
62// After losing this many consecutive samples from either sensor, we would treat the measurement as
63// stale;
64constexpr auto kMaxLostSamples = 4;
65
66// Time units for system clock ticks. This is what the Sensor Framework timestamps represent and
67// what we use for pose filtering.
68using Ticks = std::chrono::nanoseconds;
69
70// How many ticks in a second.
71constexpr auto kTicksPerSecond = Ticks::period::den;
72
73} // namespace
74
75SpatializerPoseController::SpatializerPoseController(Listener* listener,
76 std::chrono::microseconds sensorPeriod,
77 std::chrono::microseconds maxUpdatePeriod)
78 : mListener(listener),
79 mSensorPeriod(sensorPeriod),
80 mPoseProvider(SensorPoseProvider::create("headtracker", this)),
81 mProcessor(createHeadTrackingProcessor(HeadTrackingProcessor::Options{
82 .maxTranslationalVelocity = kMaxTranslationalVelocity / kTicksPerSecond,
83 .maxRotationalVelocity = kMaxRotationalVelocity / kTicksPerSecond,
84 .translationalDriftTimeConstant = Ticks(kTranslationalDriftTimeConstant).count(),
85 .rotationalDriftTimeConstant = Ticks(kRotationalDriftTimeConstant).count(),
86 .freshnessTimeout = Ticks(sensorPeriod * kMaxLostSamples).count(),
87 .predictionDuration = Ticks(kPredictionDuration).count(),
88 })),
89 mThread([this, maxUpdatePeriod] {
90 while (true) {
91 {
92 std::unique_lock lock(mMutex);
93 mCondVar.wait_for(lock, maxUpdatePeriod,
94 [this] { return mShouldExit || mShouldCalculate; });
95 if (mShouldExit) {
96 ALOGV("Exiting thread");
97 return;
98 }
99 calculate_l();
100 if (!mCalculated) {
101 mCalculated = true;
102 mCondVar.notify_all();
103 }
104 mShouldCalculate = false;
105 }
106 }
107 }) {}
108
109SpatializerPoseController::~SpatializerPoseController() {
110 {
111 std::unique_lock lock(mMutex);
112 mShouldExit = true;
113 mCondVar.notify_all();
114 }
115 mThread.join();
116}
117
118void SpatializerPoseController::setHeadSensor(const ASensor* sensor) {
119 std::lock_guard lock(mMutex);
120 // Stop current sensor, if valid.
121 if (mHeadSensor != SensorPoseProvider::INVALID_HANDLE) {
122 mPoseProvider->stopSensor(mHeadSensor);
123 }
124 // Start new sensor, if valid.
125 mHeadSensor = sensor != nullptr ? mPoseProvider->startSensor(sensor, mSensorPeriod)
126 : SensorPoseProvider::INVALID_HANDLE;
127 mProcessor->recenter();
128}
129
130void SpatializerPoseController::setScreenSensor(const ASensor* sensor) {
131 std::lock_guard lock(mMutex);
132 // Stop current sensor, if valid.
133 if (mScreenSensor != SensorPoseProvider::INVALID_HANDLE) {
134 mPoseProvider->stopSensor(mScreenSensor);
135 }
136 // Start new sensor, if valid.
137 mScreenSensor = sensor != nullptr ? mPoseProvider->startSensor(sensor, mSensorPeriod)
138 : SensorPoseProvider::INVALID_HANDLE;
139 mProcessor->recenter();
140}
141
142void SpatializerPoseController::setDesiredMode(HeadTrackingMode mode) {
143 std::lock_guard lock(mMutex);
144 mProcessor->setDesiredMode(mode);
145}
146
147void SpatializerPoseController::setScreenToStagePose(const Pose3f& screenToStage) {
148 std::lock_guard lock(mMutex);
149 mProcessor->setScreenToStagePose(screenToStage);
150}
151
152void SpatializerPoseController::setDisplayOrientation(float physicalToLogicalAngle) {
153 std::lock_guard lock(mMutex);
154 mProcessor->setDisplayOrientation(physicalToLogicalAngle);
155}
156
157void SpatializerPoseController::calculateAsync() {
158 std::lock_guard lock(mMutex);
159 mShouldCalculate = true;
160 mCondVar.notify_all();
161}
162
163void SpatializerPoseController::waitUntilCalculated() {
164 std::unique_lock lock(mMutex);
165 mCondVar.wait(lock, [this] { return mCalculated; });
166}
167
168void SpatializerPoseController::calculate_l() {
169 Pose3f headToStage;
170 HeadTrackingMode mode;
171 mProcessor->calculate(elapsedRealtimeNano());
172 headToStage = mProcessor->getHeadToStagePose();
173 mode = mProcessor->getActualMode();
174 mListener->onHeadToStagePose(headToStage);
175 if (!mActualMode.has_value() || mActualMode.value() != mode) {
176 mActualMode = mode;
177 mListener->onActualModeChange(mode);
178 }
179}
180
181void SpatializerPoseController::recenter() {
182 std::lock_guard lock(mMutex);
183 mProcessor->recenter();
184}
185
186void SpatializerPoseController::onPose(int64_t timestamp, int32_t sensor, const Pose3f& pose,
187 const std::optional<Twist3f>& twist) {
188 std::lock_guard lock(mMutex);
189 if (sensor == mHeadSensor) {
190 mProcessor->setWorldToHeadPose(timestamp, pose, twist.value_or(Twist3f()));
191 } else if (sensor == mScreenSensor) {
192 mProcessor->setWorldToScreenPose(timestamp, pose);
193 }
194}
195
196} // namespace android