blob: 92bda13952c35a93cd0cdc33dce452a31090c95e [file] [log] [blame]
John Park33858a32018-09-28 23:05:48 -07001#include "aos/util/phased_loop.h"
brians343bc112013-02-10 01:53:46 +00002
Stephan Pleinesb1177672024-05-27 17:48:32 -07003#include <compare>
4#include <ratio>
5
Austin Schuh99f7c6a2024-06-25 22:07:44 -07006#include "absl/log/check.h"
7#include "absl/log/log.h"
Austin Schuhf257f3c2019-10-27 21:00:43 -07008
Stephan Pleinesf63bde82024-01-13 15:59:33 -08009namespace aos::time {
brians343bc112013-02-10 01:53:46 +000010
Austin Schuhf257f3c2019-10-27 21:00:43 -070011PhasedLoop::PhasedLoop(const monotonic_clock::duration interval,
12 const monotonic_clock::time_point monotonic_now,
13 const monotonic_clock::duration offset)
14 : interval_(interval), offset_(offset), last_time_(offset) {
15 CHECK(offset >= monotonic_clock::duration(0));
16 CHECK(interval > monotonic_clock::duration(0));
17 CHECK(offset < interval);
18 Reset(monotonic_now);
19}
20
21void PhasedLoop::set_interval_and_offset(
22 const monotonic_clock::duration interval,
James Kuszmaul20dcc7c2023-01-20 11:06:31 -080023 const monotonic_clock::duration offset,
24 std::optional<monotonic_clock::time_point> monotonic_now) {
Milind Upadhyay42589bb2021-05-19 20:05:16 -070025 // Update last_time_ to the new offset so that we have an even interval
James Kuszmaul20dcc7c2023-01-20 11:06:31 -080026 // In doing so, set things so that last_time_ will only ever decrease on calls
27 // to set_interval_and_offset.
28 last_time_ += offset - offset_ -
29 (offset > offset_ ? interval : monotonic_clock::duration(0));
Milind Upadhyay42589bb2021-05-19 20:05:16 -070030
Austin Schuhf257f3c2019-10-27 21:00:43 -070031 interval_ = interval;
32 offset_ = offset;
33 CHECK(offset_ >= monotonic_clock::duration(0));
34 CHECK(interval_ > monotonic_clock::duration(0));
35 CHECK(offset_ < interval_);
James Kuszmaul20dcc7c2023-01-20 11:06:31 -080036 // Reset effectively clears the skipped iteration count and ensures that the
37 // last time is in the interval (monotonic_now - interval, monotonic_now],
38 // which means that a call to Iterate(monotonic_now) will return 1 and set a
39 // wakeup time after monotonic_now.
40 if (monotonic_now.has_value()) {
41 Iterate(monotonic_now.value());
42 }
Austin Schuhf257f3c2019-10-27 21:00:43 -070043}
44
45monotonic_clock::duration PhasedLoop::OffsetFromIntervalAndTime(
46 const monotonic_clock::duration interval,
47 const monotonic_clock::time_point monotonic_trigger) {
48 CHECK(interval > monotonic_clock::duration(0));
49 return monotonic_trigger.time_since_epoch() -
50 (monotonic_trigger.time_since_epoch() / interval) * interval +
51 ((monotonic_trigger.time_since_epoch() >= monotonic_clock::zero())
52 ? monotonic_clock::zero()
53 : interval);
54}
55
Austin Schuh8aec1ed2016-05-01 13:29:20 -070056int PhasedLoop::Iterate(const monotonic_clock::time_point now) {
Brian Silverman8babd8f2020-06-23 16:38:50 -070057 auto next_time = monotonic_clock::epoch();
58 // Round up to the next whole interval, ignoring offset_.
59 {
60 const auto offset_now = (now - offset_).time_since_epoch();
61 monotonic_clock::duration prerounding;
62 if (now.time_since_epoch() >= offset_) {
63 // We're above 0, so rounding up means away from 0.
64 prerounding = offset_now + interval_;
65 } else {
66 // We're below 0, so rounding up means towards 0.
67 prerounding = offset_now + monotonic_clock::duration(1);
68 }
69 next_time += (prerounding / interval_) * interval_;
70 }
71 // Add offset_ back in.
72 next_time += offset_;
Brian Silvermandcaa3f72015-11-29 05:32:08 +000073
Austin Schuh8aec1ed2016-05-01 13:29:20 -070074 const monotonic_clock::duration difference = next_time - last_time_;
Milind Upadhyay42589bb2021-05-19 20:05:16 -070075
Austin Schuh8aec1ed2016-05-01 13:29:20 -070076 const int result = difference / interval_;
Austin Schuh8aec1ed2016-05-01 13:29:20 -070077 CHECK_EQ(
78 0, (next_time - offset_).time_since_epoch().count() % interval_.count());
Brian Silverman8babd8f2020-06-23 16:38:50 -070079 CHECK(next_time > now);
Austin Schuhf257f3c2019-10-27 21:00:43 -070080 CHECK(next_time - now <= interval_);
Brian Silvermandcaa3f72015-11-29 05:32:08 +000081 last_time_ = next_time;
82 return result;
83}
84
Stephan Pleinesf63bde82024-01-13 15:59:33 -080085} // namespace aos::time