1 /*
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 "SpatializerPoseController"
19 //#define LOG_NDEBUG 0
20 #include <sensor/Sensor.h>
21 #include <utils/Log.h>
22 #include <utils/SystemClock.h>
23
24 namespace android {
25
26 using media::createHeadTrackingProcessor;
27 using media::HeadTrackingMode;
28 using media::HeadTrackingProcessor;
29 using media::Pose3f;
30 using media::SensorPoseProvider;
31 using media::Twist3f;
32
33 using namespace std::chrono_literals;
34
35 namespace {
36
37 // This is how fast, in m/s, we allow position to shift during rate-limiting.
38 constexpr auto kMaxTranslationalVelocity = 2;
39
40 // This is how fast, in rad/s, we allow rotation angle to shift during rate-limiting.
41 constexpr auto kMaxRotationalVelocity = 4 * M_PI;
42
43 // This should be set to the typical time scale that the translation sensors used drift in. This
44 // means, loosely, for how long we can trust the reading to be "accurate enough". This would
45 // determine the time constants used for high-pass filtering those readings. If the value is set
46 // too high, we may experience drift. If it is set too low, we may experience poses tending toward
47 // identity too fast.
48 constexpr auto kTranslationalDriftTimeConstant = 20s;
49
50 // This should be set to the typical time scale that the rotation sensors used drift in. This
51 // means, loosely, for how long we can trust the reading to be "accurate enough". This would
52 // determine the time constants used for high-pass filtering those readings. If the value is set
53 // too high, we may experience drift. If it is set too low, we may experience poses tending toward
54 // identity too fast.
55 constexpr auto kRotationalDriftTimeConstant = 20s;
56
57 // This is how far into the future we predict the head pose, using linear extrapolation based on
58 // twist (velocity). It should be set to a value that matches the characteristic durations of moving
59 // one's head. The higher we set this, the more latency we are able to reduce, but setting this too
60 // high will result in high prediction errors whenever the head accelerates (changes velocity).
61 constexpr auto kPredictionDuration = 10ms;
62
63 // After losing this many consecutive samples from either sensor, we would treat the measurement as
64 // stale;
65 constexpr auto kMaxLostSamples = 4;
66
67 // Time units for system clock ticks. This is what the Sensor Framework timestamps represent and
68 // what we use for pose filtering.
69 using Ticks = std::chrono::nanoseconds;
70
71 // How many ticks in a second.
72 constexpr auto kTicksPerSecond = Ticks::period::den;
73
74 } // namespace
75
SpatializerPoseController(Listener * listener,std::chrono::microseconds sensorPeriod,std::chrono::microseconds maxUpdatePeriod)76 SpatializerPoseController::SpatializerPoseController(Listener* listener,
77 std::chrono::microseconds sensorPeriod,
78 std::chrono::microseconds maxUpdatePeriod)
79 : mListener(listener),
80 mSensorPeriod(sensorPeriod),
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 mPoseProvider(SensorPoseProvider::create("headtracker", this)),
__anon0940ba190202null90 mThread([this, maxUpdatePeriod] {
91 while (true) {
92 Pose3f headToStage;
93 std::optional<HeadTrackingMode> modeIfChanged;
94 {
95 std::unique_lock lock(mMutex);
96 mCondVar.wait_for(lock, maxUpdatePeriod,
97 [this] { return mShouldExit || mShouldCalculate; });
98 if (mShouldExit) {
99 ALOGV("Exiting thread");
100 return;
101 }
102
103 // Calculate.
104 std::tie(headToStage, modeIfChanged) = calculate_l();
105 }
106
107 // Invoke the callbacks outside the lock.
108 mListener->onHeadToStagePose(headToStage);
109 if (modeIfChanged) {
110 mListener->onActualModeChange(modeIfChanged.value());
111 }
112
113 {
114 std::lock_guard lock(mMutex);
115 if (!mCalculated) {
116 mCalculated = true;
117 mCondVar.notify_all();
118 }
119 mShouldCalculate = false;
120 }
121 }
122 }) {}
123
~SpatializerPoseController()124 SpatializerPoseController::~SpatializerPoseController() {
125 {
126 std::unique_lock lock(mMutex);
127 mShouldExit = true;
128 mCondVar.notify_all();
129 }
130 mThread.join();
131 }
132
setHeadSensor(int32_t sensor)133 void SpatializerPoseController::setHeadSensor(int32_t sensor) {
134 std::lock_guard lock(mMutex);
135 // Stop current sensor, if valid and different from the other sensor.
136 if (mHeadSensor != INVALID_SENSOR && mHeadSensor != mScreenSensor) {
137 mPoseProvider->stopSensor(mHeadSensor);
138 }
139
140 if (sensor != INVALID_SENSOR) {
141 if (sensor != mScreenSensor) {
142 // Start new sensor.
143 mHeadSensor =
144 mPoseProvider->startSensor(sensor, mSensorPeriod) ? sensor : INVALID_SENSOR;
145 } else {
146 // Sensor is already enabled.
147 mHeadSensor = mScreenSensor;
148 }
149 } else {
150 mHeadSensor = INVALID_SENSOR;
151 }
152
153 mProcessor->recenter(true, false);
154 }
155
setScreenSensor(int32_t sensor)156 void SpatializerPoseController::setScreenSensor(int32_t sensor) {
157 std::lock_guard lock(mMutex);
158 // Stop current sensor, if valid and different from the other sensor.
159 if (mScreenSensor != INVALID_SENSOR && mScreenSensor != mHeadSensor) {
160 mPoseProvider->stopSensor(mScreenSensor);
161 }
162
163 if (sensor != INVALID_SENSOR) {
164 if (sensor != mHeadSensor) {
165 // Start new sensor.
166 mScreenSensor =
167 mPoseProvider->startSensor(sensor, mSensorPeriod) ? sensor : INVALID_SENSOR;
168 } else {
169 // Sensor is already enabled.
170 mScreenSensor = mHeadSensor;
171 }
172 } else {
173 mScreenSensor = INVALID_SENSOR;
174 }
175
176 mProcessor->recenter(false, true);
177 }
178
setDesiredMode(HeadTrackingMode mode)179 void SpatializerPoseController::setDesiredMode(HeadTrackingMode mode) {
180 std::lock_guard lock(mMutex);
181 mProcessor->setDesiredMode(mode);
182 }
183
setScreenToStagePose(const Pose3f & screenToStage)184 void SpatializerPoseController::setScreenToStagePose(const Pose3f& screenToStage) {
185 std::lock_guard lock(mMutex);
186 mProcessor->setScreenToStagePose(screenToStage);
187 }
188
setDisplayOrientation(float physicalToLogicalAngle)189 void SpatializerPoseController::setDisplayOrientation(float physicalToLogicalAngle) {
190 std::lock_guard lock(mMutex);
191 mProcessor->setDisplayOrientation(physicalToLogicalAngle);
192 }
193
calculateAsync()194 void SpatializerPoseController::calculateAsync() {
195 std::lock_guard lock(mMutex);
196 mShouldCalculate = true;
197 mCondVar.notify_all();
198 }
199
waitUntilCalculated()200 void SpatializerPoseController::waitUntilCalculated() {
201 std::unique_lock lock(mMutex);
202 mCondVar.wait(lock, [this] { return mCalculated; });
203 }
204
205 std::tuple<media::Pose3f, std::optional<media::HeadTrackingMode>>
calculate_l()206 SpatializerPoseController::calculate_l() {
207 Pose3f headToStage;
208 HeadTrackingMode mode;
209 std::optional<media::HeadTrackingMode> modeIfChanged;
210
211 mProcessor->calculate(elapsedRealtimeNano());
212 headToStage = mProcessor->getHeadToStagePose();
213 mode = mProcessor->getActualMode();
214 if (!mActualMode.has_value() || mActualMode.value() != mode) {
215 mActualMode = mode;
216 modeIfChanged = mode;
217 }
218 return std::make_tuple(headToStage, modeIfChanged);
219 }
220
recenter()221 void SpatializerPoseController::recenter() {
222 std::lock_guard lock(mMutex);
223 mProcessor->recenter();
224 }
225
onPose(int64_t timestamp,int32_t sensor,const Pose3f & pose,const std::optional<Twist3f> & twist,bool isNewReference)226 void SpatializerPoseController::onPose(int64_t timestamp, int32_t sensor, const Pose3f& pose,
227 const std::optional<Twist3f>& twist, bool isNewReference) {
228 std::lock_guard lock(mMutex);
229 if (sensor == mHeadSensor) {
230 mProcessor->setWorldToHeadPose(timestamp, pose, twist.value_or(Twist3f()));
231 if (isNewReference) {
232 mProcessor->recenter(true, false);
233 }
234 }
235 if (sensor == mScreenSensor) {
236 mProcessor->setWorldToScreenPose(timestamp, pose);
237 if (isNewReference) {
238 mProcessor->recenter(false, true);
239 }
240 }
241 }
242
243 } // namespace android
244