Ocean
Loading...
Searching...
No Matches
SampleMap.h
Go to the documentation of this file.
1/*
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 */
7
8#ifndef META_OCEAN_MATH_SAMPLE_MAP_H
9#define META_OCEAN_MATH_SAMPLE_MAP_H
10
11#include "ocean/math/Math.h"
13#include "ocean/math/Numeric.h"
14
15#include "ocean/base/Lock.h"
16
17namespace Ocean
18{
19
20/**
21 * This class stores samples of e.g., sensor or tracking data in a map.
22 * Samples are stored in a round robin method.<br>
23 * Whenever the map does not have an empty spot left, the oldest sample will be replaced by the newest sample.<br>
24 * The implementation is thread-safe.
25 * @tparam T The type of the sample to be stored
26 * @ingroup math
27 */
28template <typename T>
30{
31 public:
32
33 /**
34 * Definition of a map mapping timestamps to samples.
35 */
36 typedef std::map<double, T> Map;
37
38 /**
39 * Definition of individual interpolation strategies for samples.
40 */
42 {
43 /// An invalid strategy.
45 /// The sample with nearest/closest timestamp is used.
47 /// The sample is interpolated based on two samples.
49 };
50
51 public:
52
53 /**
54 * Creates a new map with default capacity.
55 */
56 SampleMap() noexcept;
57
58 /**
59 * Creates a new amp with specified capacity.
60 * @param capacity The number of samples the new map can store, with range [1, infinity), -1 to create a map without capacity restrictions
61 */
62 explicit SampleMap(const size_t capacity) noexcept;
63
64 /**
65 * Copy constructor.
66 * @param sampleMap The map to be copied
67 */
68 SampleMap(const SampleMap& sampleMap) noexcept;
69
70 /**
71 * Move constructor.
72 * @param sampleMap The map to be moved
73 */
74 SampleMap(SampleMap&& sampleMap) noexcept;
75
76 /**
77 * Inserts a new sample with corresponding timestamp.
78 * In case, a sample with same timestamp exists already, the given sample will replace the existing sample.<br>
79 * In case, the map does not have an empty spot left, the oldest sample will be replaced by the given sample.
80 * @param sample The new sample to be inserted, must be valid
81 * @param timestamp The timestamp of the sample, must be valid
82 */
83 void insert(const T& sample, const double& timestamp);
84
85 /**
86 * Returns the most recent sample.
87 * @param value The resulting sample
88 * @param sampleTimestamp The timestamp of the returning sample; nullptr if not of interest
89 * @return True, if succeeded
90 */
91 bool sample(T& value, double* sampleTimestamp = nullptr) const;
92
93 /**
94 * Returns the sample with a specific timestamp.
95 * @param timestamp Timestamp of the sample to be returned
96 * @param value The resulting sample with the requested timestamp
97 * @return True, if succeeded (if a sample with the timestamp existed)
98 */
99 bool sample(const double& timestamp, T& value) const;
100
101 /**
102 * Returns the sample best matching with a specified timestamp.
103 * In case, the given timestamp does not fit to an existing sample, the resulting sample will be based on the specified interpolation strategy.
104 * @param timestamp The timestamp for which a sample will be determined, must be valid
105 * @param interpolationStrategy The interpolation strategy to be applied in case the timestamp does not fit with an existing sample
106 * @param value The resulting sample
107 * @param timestampDistance Optional resulting minimal time distance between the requested timestamp and the sample(s) used to create the resulting value, with range [0, infinity)
108 * @param sampleTimestamp The timestamp of the returning sample; nullptr if not of interest
109 * @return True, if a sample could be determined
110 */
111 bool sample(const double& timestamp, const InterpolationStrategy interpolationStrategy, T& value, double* timestampDistance = nullptr, double* sampleTimestamp = nullptr) const;
112
113 /**
114 * Returns all samples stores in this map.
115 * @return All samples as vector of pairs
116 */
117 std::vector<std::pair<double, T>> samples() const;
118
119 /**
120 * Returns all samples stores in this map.
121 * @return All samples as a standard map
122 */
123 Map data() const;
124
125 /**
126 * Returns the number of samples currently stored in this map.
127 * @return The map's samples, with range [0, capacity()]
128 */
129 inline size_t size() const;
130
131 /**
132 * Returns the capacity of this map (the number of samples this map can store).
133 * The capacity of this map, with range [1, infinity)
134 */
135 inline size_t capacity() const;
136
137 /**
138 * Returns whether this map is empty.
139 * @return True, if so
140 */
141 inline bool isEmpty() const;
142
143 /**
144 * Removes all samples from this map.
145 */
146 inline void clear();
147
148 /**
149 * Copy operator.
150 * @param sampleMap The map to be copied
151 * @return Reference to this object
152 */
153 SampleMap<T>& operator=(const SampleMap<T>& sampleMap) noexcept;
154
155 /**
156 * Move operator.
157 * @param sampleMap The map to be moved
158 * @return Reference to this object
159 */
160 SampleMap<T>& operator=(SampleMap<T>&& sampleMap) noexcept;
161
162 protected:
163
164 /// The map holding the actual samples.
166
167 /// The capacity of this map, with range [1, infinity).
168 size_t capacity_;
169
170 /// The lock of this map.
171 mutable Lock lock_;
172};
173
174template <typename T>
175SampleMap<T>::SampleMap() noexcept :
176 capacity_(100)
177{
178 // nothing to do here
179}
180
181template <typename T>
182SampleMap<T>::SampleMap(const size_t capacity) noexcept :
184{
185 ocean_assert(capacity_ >= 1);
186}
187
188template <typename T>
189SampleMap<T>::SampleMap(const SampleMap& sampleMap) noexcept :
190 map_(sampleMap.map_),
191 capacity_(sampleMap.capacity_)
192{
193 // nothing to do here
194}
195
196template <typename T>
198{
199 *this = std::move(sampleMap);
200}
201
202template <typename T>
203void SampleMap<T>::insert(const T& sample, const double& timestamp)
204{
205 const ScopedLock scopedLock(lock_);
206
207 if (map_.size() >= capacity_)
208 {
209 typename Map::iterator i = map_.begin();
210 ocean_assert(i != map_.end());
211
212 map_.erase(i);
213 }
214
215 map_[timestamp] = sample; // not using insert to ensure that identical timestamps overwrite previous entries
216}
217
218template <typename T>
219bool SampleMap<T>::sample(T& value, double* sampleTimestamp) const
220{
221 const ScopedLock scopedLock(lock_);
222
223 // looking for the most recent sample
224 typename Map::const_reverse_iterator i = map_.rbegin();
225
226 if (i == map_.rend())
227 {
228 return false;
229 }
230
231 value = i->second;
232
233 if (sampleTimestamp)
234 {
235 *sampleTimestamp = i->first;
236 }
237
238 return true;
239}
240
241template <typename T>
242bool SampleMap<T>::sample(const double& timestamp, T& value) const
243{
244 const ScopedLock scopedLock(lock_);
245
246 const typename Map::const_iterator i = map_.find(timestamp);
247
248 if (i == map_.cend())
249 {
250 return false;
251 }
252
253 value = i->second;
254
255 return true;
256}
257
258template <typename T>
259bool SampleMap<T>::sample(const double& timestamp, const InterpolationStrategy interpolationStrategy, T& value, double* timestampDistance, double* sampleTimestamp) const
260{
261 const ScopedLock scopedLock(lock_);
262
263 if (map_.empty())
264 {
265 return false;
266 }
267
268 if (map_.size() == 1)
269 {
270 // we just have one sample, so we return this one without any further handling
271
272 ocean_assert(map_.rbegin() != map_.rend());
273 value = map_.rbegin()->second;
274
275 if (timestampDistance)
276 {
277 // distance is |timestamp - sample.timestamp|
278 *timestampDistance = NumericD::abs(timestamp - map_.rbegin()->first);
279 }
280
281 if (sampleTimestamp)
282 {
283 *sampleTimestamp = map_.rbegin()->first;
284 }
285
286 return true;
287 }
288
289 // let's find the sample with timestamp bigger (younger) than the specified timestamp
290 const typename Map::const_iterator iUpper = map_.upper_bound(timestamp);
291
292 if (iUpper == map_.cend())
293 {
294 // our timestamp is too new so that we simply return the sample of the most recent timestamp
295
296 ocean_assert(map_.rbegin() != map_.rend());
297 value = map_.rbegin()->second;
298
299 if (timestampDistance)
300 {
301 // distance is |timestamp - newestSample.timestamp|
302 *timestampDistance = NumericD::abs(timestamp - map_.rbegin()->first);
303 }
304
305 if (sampleTimestamp)
306 {
307 *sampleTimestamp = map_.rbegin()->first;
308 }
309
310 return true;
311 }
312
313 ocean_assert(iUpper->first > timestamp);
314
315 if (iUpper == map_.cbegin())
316 {
317 // our timestamp is too old so that we simply return the oldest sample we have
318
319 ocean_assert(map_.begin() != map_.end());
320
321 value = map_.begin()->second;
322
323 if (timestampDistance)
324 {
325 // distance is |timestamp - oldestSample.timestamp|
326 *timestampDistance = NumericD::abs(timestamp - map_.begin()->first);
327 }
328
329 if (sampleTimestamp)
330 {
331 *sampleTimestamp = map_.begin()->first;
332 }
333
334 return true;
335 }
336
337 typename Map::const_iterator iLower(iUpper);
338
339 ocean_assert(iLower != map_.begin());
340 iLower--;
341
342 ocean_assert(iLower->first <= timestamp);
343
344 const double lowerDelta = timestamp - iLower->first;
345 const double upperDelta = iUpper->first - timestamp;
346 ocean_assert(lowerDelta >= 0.0 && upperDelta >= 0.0);
347
348 if (interpolationStrategy == IS_TIMESTAMP_INTERPOLATE)
349 {
350 const double delta = lowerDelta + upperDelta;
351
352 if (NumericD::isEqualEps(delta))
353 {
354 // both samples are almost identical, so that we return the sample from the past (not from the future)
355 value = iLower->second;
356
357 if (timestampDistance)
358 {
359 // distance is 0
360 *timestampDistance = 0.0;
361 }
362
363 if (sampleTimestamp)
364 {
365 *sampleTimestamp = iLower->first;
366 }
367
368 return true;
369 }
370
371 const double interpolationFactor = lowerDelta / delta;
372 ocean_assert(interpolationFactor >= 0.0 && interpolationFactor <= 1.0);
373
374 const T& lowerSample = iLower->second;
375 const T& upperSample = iUpper->second;
376
377 value = Interpolation::linear(lowerSample, upperSample, interpolationFactor);
378
379 if (timestampDistance)
380 {
381 // distance is min(|upperTimestamp - timestamp|, |timestamp - lowerTimestamp|)
382 *timestampDistance = std::min(iUpper->first - timestamp, timestamp - iLower->first);
383 }
384
385 if (sampleTimestamp)
386 {
387 *sampleTimestamp = iLower->first * interpolationFactor + iUpper->first * (1.0 - interpolationFactor);
388 }
389
390 return true;
391 }
392
393 ocean_assert(interpolationStrategy == IS_TIMESTAMP_NEAREST);
394
395 // let's return the sample with timestamp closest to the specified timestamp
396
397 if (lowerDelta < upperDelta)
398 {
399 value = iLower->second;
400
401 if (timestampDistance)
402 {
403 // distance is min(|upperTimestamp - timestamp|, |timestamp - lowerTimestamp|)
404 *timestampDistance = std::min(iUpper->first - timestamp, timestamp - iLower->first);
405 }
406
407 if (sampleTimestamp)
408 {
409 *sampleTimestamp = iLower->first;
410 }
411
412 return true;
413 }
414
415 value = iUpper->second;
416
417 if (timestampDistance)
418 {
419 // distance is min(|upperTimestamp - timestamp|, |timestamp - lowerTimestamp|)
420 *timestampDistance = std::min(iUpper->first - timestamp, timestamp - iLower->first);
421 }
422
423 if (sampleTimestamp)
424 {
425 *sampleTimestamp = iUpper->first;
426 }
427
428 return true;
429}
430
431template <typename T>
432std::vector<std::pair<double, T>> SampleMap<T>::samples() const
433{
434 const ScopedLock scopedLock(lock_);
435
436 std::vector<std::pair<double, T>> result;
437 result.reserve(map_.size());
438
439 for (typename Map::const_iterator i = map_.cbegin(); i != map_.cend(); ++i)
440 {
441 result.emplace_back(i->first, i->second);
442 }
443
444 return result;
445}
446
447template <typename T>
449{
450 const ScopedLock scopedLock(lock_);
451
452 Map result(map_);
453
454 return result;
455}
456
457template <typename T>
458inline size_t SampleMap<T>::size() const
459{
460 const ScopedLock scopedLock(lock_);
461
462 return map_.size();
463}
464
465template <typename T>
466inline size_t SampleMap<T>::capacity() const
467{
468 const ScopedLock scopedLock(lock_);
469
470 return capacity_;
471}
472
473template <typename T>
474inline bool SampleMap<T>::isEmpty() const
475{
476 const ScopedLock scopedLock(lock_);
477
478 return map_.empty();
479}
480
481template <typename T>
483{
484 const ScopedLock scopedLock(lock_);
485
486 map_.clear();
487}
488
489template <typename T>
491{
492 map_ = sampleMap.map_;
493 capacity_ = sampleMap.capacity_;
494
495 return *this;
496}
497
498template <typename T>
500{
501 if (this != &sampleMap)
502 {
503 map_ = std::move(sampleMap.map_);
504
505 capacity_ = sampleMap.capacity_;
506 sampleMap.capacity_ = 0;
507 }
508
509 return *this;
510}
511
512}
513
514#endif // META_OCEAN_MATH_SAMPLE_MAP_H
static T linear(const T &v0, const T &v1, const TFactor &t)
Performs a linear interpolation between two values.
Definition Interpolation.h:508
This class implements a recursive lock object.
Definition Lock.h:31
static T abs(const T value)
Returns the absolute value of a given value.
Definition Numeric.h:1220
static constexpr bool isEqualEps(const T value)
Returns whether a value is smaller than or equal to a small epsilon.
Definition Numeric.h:2087
This class stores samples of e.g., sensor or tracking data in a map.
Definition SampleMap.h:30
Lock lock_
The lock of this map.
Definition SampleMap.h:171
size_t size() const
Returns the number of samples currently stored in this map.
Definition SampleMap.h:458
Map data() const
Returns all samples stores in this map.
Definition SampleMap.h:448
std::map< double, T > Map
Definition of a map mapping timestamps to samples.
Definition SampleMap.h:36
bool isEmpty() const
Returns whether this map is empty.
Definition SampleMap.h:474
Map map_
The map holding the actual samples.
Definition SampleMap.h:165
void clear()
Removes all samples from this map.
Definition SampleMap.h:482
std::vector< std::pair< double, T > > samples() const
Returns all samples stores in this map.
Definition SampleMap.h:432
void insert(const T &sample, const double &timestamp)
Inserts a new sample with corresponding timestamp.
Definition SampleMap.h:203
SampleMap< T > & operator=(const SampleMap< T > &sampleMap) noexcept
Copy operator.
Definition SampleMap.h:490
size_t capacity() const
Returns the capacity of this map (the number of samples this map can store).
Definition SampleMap.h:466
bool sample(T &value, double *sampleTimestamp=nullptr) const
Returns the most recent sample.
Definition SampleMap.h:219
SampleMap() noexcept
Creates a new map with default capacity.
Definition SampleMap.h:175
InterpolationStrategy
Definition of individual interpolation strategies for samples.
Definition SampleMap.h:42
@ IS_TIMESTAMP_NEAREST
The sample with nearest/closest timestamp is used.
Definition SampleMap.h:46
@ IS_INVALID
An invalid strategy.
Definition SampleMap.h:44
@ IS_TIMESTAMP_INTERPOLATE
The sample is interpolated based on two samples.
Definition SampleMap.h:48
size_t capacity_
The capacity of this map, with range [1, infinity).
Definition SampleMap.h:168
This class implements a scoped lock object for recursive lock objects.
Definition Lock.h:135
The namespace covering the entire Ocean framework.
Definition Accessor.h:15