blob: aa2fe804678e5ea9d096faa4034d4aac93398f84 [file] [log] [blame]
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -07001/*
2 * Copyright (C) 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#pragma once
17
18#include <optional>
19
20#include "media/Pose.h"
21
22namespace android {
23namespace media {
24
25/**
26 * Limits a stream of poses to a given maximum translational and rotational velocities.
27 *
28 * Normal operation:
29 *
30 * Pose3f output;
31 * PoseRateLimiter limiter(...);
32 *
33 * // Limiting is disabled. Output will be the same as last input.
34 * limiter.setTarget(...);
35 * output = limiter.calculatePose(...);
36 * limiter.setTarget(...);
37 * output = limiter.calculatePose(...);
38 *
39 * // Enable limiting. Output will no longer be necessarily the same as last input.
40 * limiter.enable();
41 * limiter.setTarget(...);
42 * output = limiter.calculatePose(...);
43 * limiter.setTarget(...);
44 * output = limiter.calculatePose(...);
45 *
46 * // When eventually the output has been able to catch up with the last input, the limited will be
47 * // automatically disabled again and the output will match the input again.
48 * limiter.setTarget(...);
49 * output = limiter.calculatePose(...);
50 *
51 * As shown above, the limiter is turned on manually via enable(), but turns off automatically as
52 * soon as the output is able to catch up to the input. The intention is that rate limiting will be
53 * turned on at specific times to smooth out any artificial discontinuities introduced to the pose
54 * stream, but the rest of the time will be a simple passthrough.
55
56 * setTarget(...) and calculatePose(...) don't have to be ordered in any particular way. However,
57 * setTarget or reset() must be called at least once prior to the first calculatePose().
58 *
59 * Calling reset() instead of setTarget() forces the output to the given pose and disables rate
60 * limiting.
61 *
62 * This implementation is thread-compatible, but not thread-safe.
63 */
64class PoseRateLimiter {
65 public:
66 struct Options {
67 float maxTranslationalVelocity = std::numeric_limits<float>::infinity();
68 float maxRotationalVelocity = std::numeric_limits<float>::infinity();
69 };
70
71 explicit PoseRateLimiter(const Options& options);
72
73 void enable();
74
75 void reset(const Pose3f& target);
76 void setTarget(const Pose3f& target);
77
78 Pose3f calculatePose(int64_t timestamp);
79
80 private:
81 struct Point {
82 Pose3f pose;
83 int64_t timestamp;
84 };
85
86 const Options mOptions;
87 bool mLimiting;
88 std::optional<Pose3f> mTargetPose;
89 std::optional<Point> mOutput;
90};
91
92} // namespace media
93} // namespace android