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