blob: 3c88931fb1450c36fc0f3e8b8f42bc29bf1ed16e [file] [log] [blame]
John Park33858a32018-09-28 23:05:48 -07001#include "aos/time/time.h"
brians343bc112013-02-10 01:53:46 +00002
Stephan Pleinescd3701f2024-05-30 10:56:04 -07003#include <ctype.h>
4#include <errno.h>
5#include <sys/time.h>
6
Austin Schuh3ce0a922020-07-21 21:08:54 -07007#include <algorithm>
Austin Schuhf2a50ba2016-12-24 16:16:26 -08008#include <chrono>
Stephan Pleinescd3701f2024-05-30 10:56:04 -07009#include <compare>
10#include <cstdint>
James Kuszmaul38735e82019-12-07 16:42:06 -080011#include <ctime>
Austin Schuhe92f7e62019-12-25 13:54:41 -080012#include <iomanip>
Stephan Pleinescd3701f2024-05-30 10:56:04 -070013#include <ratio>
Eric Schmiedeberg42273f42023-12-14 13:48:45 -070014#include <sstream>
Brian Silvermand0575692015-02-21 16:24:02 -050015
Brian Silvermanc03a30c2019-02-16 18:21:56 -080016#ifdef __linux__
17
Austin Schuh99f7c6a2024-06-25 22:07:44 -070018#include "absl/log/check.h"
19#include "absl/log/log.h"
Austin Schuhd90499f2023-03-24 15:10:51 -070020#include "absl/strings/numbers.h"
brians343bc112013-02-10 01:53:46 +000021
Brian Silvermanc03a30c2019-02-16 18:21:56 -080022#else // __linux__
23
24#include "motors/core/kinetis.h"
25
26// The systick interrupt increments this every 1ms.
27extern "C" volatile uint32_t systick_millis_count;
28
29#endif // __linux__
30
Austin Schuhf2a50ba2016-12-24 16:16:26 -080031namespace chrono = ::std::chrono;
32
Philipp Schradera7878b92023-10-16 17:33:40 -070033namespace {
34
35void PrintToStream(std::ostream &stream, chrono::nanoseconds duration) {
36 chrono::seconds seconds = chrono::duration_cast<chrono::seconds>(duration);
37 if (duration < chrono::nanoseconds(0)) {
38 stream << "-" << -seconds.count() << "." << std::setfill('0')
39 << std::setw(9)
40 << chrono::duration_cast<chrono::nanoseconds>(seconds - duration)
41 .count()
42 << "sec";
43 } else {
44 stream << seconds.count() << "." << std::setfill('0') << std::setw(9)
45 << chrono::duration_cast<chrono::nanoseconds>(duration - seconds)
46 .count()
47 << "sec";
48 }
49}
50
51} // namespace
52
Brian Silvermanc03a30c2019-02-16 18:21:56 -080053#ifdef __linux__
54
Alexei Strots08b94232024-05-24 16:48:07 -070055namespace aos::this_thread {
56
Austin Schuh793d6b92016-05-01 13:28:14 -070057void sleep_until(const ::aos::monotonic_clock::time_point &end_time) {
58 struct timespec end_time_timespec;
59 ::std::chrono::seconds sec =
60 ::std::chrono::duration_cast<::std::chrono::seconds>(
61 end_time.time_since_epoch());
62 ::std::chrono::nanoseconds nsec =
63 ::std::chrono::duration_cast<::std::chrono::nanoseconds>(
64 end_time.time_since_epoch() - sec);
65 end_time_timespec.tv_sec = sec.count();
66 end_time_timespec.tv_nsec = nsec.count();
67 int returnval;
68 do {
69 returnval = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME,
70 &end_time_timespec, nullptr);
Austin Schuhf257f3c2019-10-27 21:00:43 -070071 PCHECK(returnval == 0 || returnval == EINTR)
72 << ": clock_nanosleep(" << static_cast<uintmax_t>(CLOCK_MONOTONIC)
73 << ", TIMER_ABSTIME, " << &end_time_timespec << ", nullptr) failed";
Austin Schuh793d6b92016-05-01 13:28:14 -070074 } while (returnval != 0);
75}
76
Alexei Strots08b94232024-05-24 16:48:07 -070077} // namespace aos::this_thread
Austin Schuh793d6b92016-05-01 13:28:14 -070078
Brian Silvermanc03a30c2019-02-16 18:21:56 -080079#endif // __linux__
Austin Schuh793d6b92016-05-01 13:28:14 -070080
brians343bc112013-02-10 01:53:46 +000081namespace aos {
Austin Schuhde8a8ff2019-11-30 15:25:36 -080082
James Kuszmaul38735e82019-12-07 16:42:06 -080083std::ostream &operator<<(std::ostream &stream,
84 const aos::monotonic_clock::time_point &now) {
Philipp Schradera7878b92023-10-16 17:33:40 -070085 PrintToStream(stream, now.time_since_epoch());
Austin Schuh40102d22019-12-27 23:30:06 -080086 return stream;
87}
88
Eric Schmiedeberg42273f42023-12-14 13:48:45 -070089std::string ToString(const aos::monotonic_clock::time_point &now) {
90 std::ostringstream stream;
91 stream << now;
92 return stream.str();
93}
94
95std::string ToString(const aos::realtime_clock::time_point &now) {
96 std::ostringstream stream;
97 stream << now;
98 return stream.str();
99}
100
Austin Schuhd90499f2023-03-24 15:10:51 -0700101#ifdef __linux__
Austin Schuh3ce0a922020-07-21 21:08:54 -0700102std::optional<monotonic_clock::time_point> monotonic_clock::FromString(
103 const std::string_view now) {
104 // This should undo the operator << above.
105 if (now.size() < 14) {
106 return std::nullopt;
107 }
108
109 if (now.substr(now.size() - 3, now.size()) != "sec") {
110 return std::nullopt;
111 }
112
Brian J Griglak259048b2022-01-27 12:30:55 -0700113 if (now[now.size() - 13] != '.') {
Austin Schuh3ce0a922020-07-21 21:08:54 -0700114 return std::nullopt;
115 }
116
Brian J Griglak259048b2022-01-27 12:30:55 -0700117 bool negative = now[0] == '-';
Austin Schuh3ce0a922020-07-21 21:08:54 -0700118
Austin Schuhd90499f2023-03-24 15:10:51 -0700119 std::string_view sec(
Austin Schuh3ce0a922020-07-21 21:08:54 -0700120 now.substr(negative ? 1 : 0, now.size() - (negative ? 14 : 13)));
Austin Schuhd90499f2023-03-24 15:10:51 -0700121 std::string_view nsec(now.substr(now.size() - 12, 9));
Austin Schuh3ce0a922020-07-21 21:08:54 -0700122
123 if (!std::all_of(sec.begin(), sec.end(), ::isdigit) ||
124 !std::all_of(nsec.begin(), nsec.end(), ::isdigit)) {
125 return std::nullopt;
126 }
127
Austin Schuhd90499f2023-03-24 15:10:51 -0700128 std::chrono::seconds::rep seconds_data;
129 if (!absl::SimpleAtoi(sec, &seconds_data)) {
130 return std::nullopt;
131 }
132
133 std::chrono::nanoseconds::rep nanoseconds_data;
134 if (!absl::SimpleAtoi(nsec, &nanoseconds_data)) {
135 return std::nullopt;
136 }
137
Austin Schuh3ce0a922020-07-21 21:08:54 -0700138 return monotonic_clock::time_point(
Austin Schuhd90499f2023-03-24 15:10:51 -0700139 std::chrono::seconds((negative ? -1 : 1) * seconds_data) +
140 std::chrono::nanoseconds((negative ? -1 : 1) * nanoseconds_data));
Austin Schuh3ce0a922020-07-21 21:08:54 -0700141}
142
143std::optional<realtime_clock::time_point> realtime_clock::FromString(
144 const std::string_view now) {
145 // This should undo the operator << above.
146
147 if (now.size() < 25) {
148 return std::nullopt;
149 }
150
Brian J Griglak259048b2022-01-27 12:30:55 -0700151 if (now[now.size() - 10] != '.') {
Austin Schuh3ce0a922020-07-21 21:08:54 -0700152 return std::nullopt;
153 }
154
Austin Schuhd90499f2023-03-24 15:10:51 -0700155 std::string_view nsec(now.substr(now.size() - 9, 9));
Austin Schuh3ce0a922020-07-21 21:08:54 -0700156
157 if (!std::all_of(nsec.begin(), nsec.end(), ::isdigit)) {
158 return std::nullopt;
159 }
160
161 struct tm tm;
162 std::istringstream ss(std::string(now.substr(0, now.size() - 10)));
163 ss >> std::get_time(&tm, "%Y-%m-%d_%H-%M-%S");
Austin Schuh76a313c2021-11-30 19:16:35 -0800164 if (ss.fail()) {
165 return std::nullopt;
166 }
Austin Schuh3ce0a922020-07-21 21:08:54 -0700167 tm.tm_isdst = -1;
168
169 time_t seconds = mktime(&tm);
170
Austin Schuhd90499f2023-03-24 15:10:51 -0700171 std::chrono::nanoseconds::rep nanoseconds_data;
172 if (!absl::SimpleAtoi(nsec, &nanoseconds_data)) {
173 return std::nullopt;
174 }
175
Philipp Schrader790cb542023-07-05 21:06:52 -0700176 return realtime_clock::time_point(std::chrono::seconds(seconds) +
177 std::chrono::nanoseconds(nanoseconds_data));
Austin Schuh3ce0a922020-07-21 21:08:54 -0700178}
Austin Schuhd90499f2023-03-24 15:10:51 -0700179#endif
Austin Schuh3ce0a922020-07-21 21:08:54 -0700180
Austin Schuh40102d22019-12-27 23:30:06 -0800181std::ostream &operator<<(std::ostream &stream,
182 const aos::realtime_clock::time_point &now) {
183 std::tm tm;
184 std::chrono::seconds seconds =
185 now < realtime_clock::epoch()
Austin Schuhb2ac0562021-09-13 23:23:33 -0700186 ? (std::chrono::duration_cast<std::chrono::seconds>(
187 now.time_since_epoch() + std::chrono::nanoseconds(1)) -
188 std::chrono::seconds(1))
Austin Schuh40102d22019-12-27 23:30:06 -0800189 : std::chrono::duration_cast<std::chrono::seconds>(
Austin Schuhe92f7e62019-12-25 13:54:41 -0800190 now.time_since_epoch());
191
Philipp Schraderd0d264d2023-12-20 11:11:35 -0800192 // We can run into some corner cases where the seconds value is large enough
193 // to cause the conversion to nanoseconds to overflow. That is undefined
194 // behaviour so we prevent it with this check here.
195 if (int64_t result;
196 __builtin_mul_overflow(seconds.count(), 1'000'000'000, &result)) {
197 stream << "(unrepresentable realtime " << now.time_since_epoch().count()
198 << ")";
199 return stream;
200 }
201
Austin Schuh40102d22019-12-27 23:30:06 -0800202 std::time_t seconds_t = seconds.count();
203 stream << std::put_time(localtime_r(&seconds_t, &tm), "%Y-%m-%d_%H-%M-%S.")
204 << std::setfill('0') << std::setw(9)
205 << std::chrono::duration_cast<std::chrono::nanoseconds>(
206 now.time_since_epoch() - seconds)
207 .count();
208 return stream;
Austin Schuhde8a8ff2019-11-30 15:25:36 -0800209}
210
brians343bc112013-02-10 01:53:46 +0000211namespace time {
212
Austin Schuh40102d22019-12-27 23:30:06 -0800213struct timespec to_timespec(const ::aos::monotonic_clock::duration duration) {
Neil Balch229001a2018-01-07 18:22:52 -0800214 struct timespec time_timespec;
215 ::std::chrono::seconds sec =
216 ::std::chrono::duration_cast<::std::chrono::seconds>(duration);
217 ::std::chrono::nanoseconds nsec =
218 ::std::chrono::duration_cast<::std::chrono::nanoseconds>(duration - sec);
219 time_timespec.tv_sec = sec.count();
220 time_timespec.tv_nsec = nsec.count();
221 return time_timespec;
222}
223
Austin Schuh40102d22019-12-27 23:30:06 -0800224struct timespec to_timespec(const ::aos::monotonic_clock::time_point time) {
Neil Balch229001a2018-01-07 18:22:52 -0800225 return to_timespec(time.time_since_epoch());
226}
Brian Silverman967e5df2020-02-09 16:43:34 -0800227
228::aos::monotonic_clock::time_point from_timeval(struct timeval t) {
229 return monotonic_clock::epoch() + std::chrono::seconds(t.tv_sec) +
230 std::chrono::microseconds(t.tv_usec);
231}
232
brians343bc112013-02-10 01:53:46 +0000233} // namespace time
Austin Schuh858c0212016-11-25 17:23:30 -0800234
235constexpr monotonic_clock::time_point monotonic_clock::min_time;
Brian Silverman94357272019-02-23 21:00:54 -0800236constexpr monotonic_clock::time_point monotonic_clock::max_time;
Brian Silverman2eb89762019-02-17 15:16:37 -0800237constexpr realtime_clock::time_point realtime_clock::min_time;
Brian Silverman94357272019-02-23 21:00:54 -0800238constexpr realtime_clock::time_point realtime_clock::max_time;
Austin Schuh858c0212016-11-25 17:23:30 -0800239
240monotonic_clock::time_point monotonic_clock::now() noexcept {
Brian Silvermanc03a30c2019-02-16 18:21:56 -0800241#ifdef __linux__
Austin Schuh858c0212016-11-25 17:23:30 -0800242 struct timespec current_time;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700243 PCHECK(clock_gettime(CLOCK_MONOTONIC, &current_time) == 0)
244 << ": clock_gettime(" << static_cast<uintmax_t>(CLOCK_MONOTONIC) << ", "
245 << &current_time << ") failed";
246
Austin Schuh858c0212016-11-25 17:23:30 -0800247 return time_point(::std::chrono::seconds(current_time.tv_sec) +
Alex Perrycb7da4b2019-08-28 19:35:56 -0700248 ::std::chrono::nanoseconds(current_time.tv_nsec));
Brian Silvermanc03a30c2019-02-16 18:21:56 -0800249
250#else // __linux__
251
252 __disable_irq();
253 const uint32_t current_counter = SYST_CVR;
254 uint32_t ms_count = systick_millis_count;
255 const uint32_t istatus = SCB_ICSR;
256 __enable_irq();
257 // If the interrupt is pending and the timer has already wrapped from 0 back
258 // up to its max, then add another ms.
259 if ((istatus & SCB_ICSR_PENDSTSET) && current_counter > 50) {
260 ++ms_count;
261 }
262
263 // It counts down, but everything we care about counts up.
264 const uint32_t counter_up = ((F_CPU / 1000) - 1) - current_counter;
265
266 // "3.2.1.2 System Tick Timer" in the TRM says "The System Tick Timer's clock
267 // source is always the core clock, FCLK".
268 using systick_duration =
269 std::chrono::duration<uint32_t, std::ratio<1, F_CPU>>;
270
Milo Linfd40acb2021-11-06 14:51:36 -0700271 return time_point(std::chrono::round<std::chrono::nanoseconds>(
Brian Silvermanc03a30c2019-02-16 18:21:56 -0800272 std::chrono::milliseconds(ms_count) + systick_duration(counter_up)));
273
274#endif // __linux__
Austin Schuh858c0212016-11-25 17:23:30 -0800275}
276
Brian Silvermana3688802019-02-16 19:31:26 -0800277#ifdef __linux__
278realtime_clock::time_point realtime_clock::now() noexcept {
279 struct timespec current_time;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700280 PCHECK(clock_gettime(CLOCK_REALTIME, &current_time) == 0)
281 << "clock_gettime(" << static_cast<uintmax_t>(CLOCK_REALTIME) << ", "
282 << &current_time << ") failed";
Brian Silvermana3688802019-02-16 19:31:26 -0800283
284 return time_point(::std::chrono::seconds(current_time.tv_sec) +
285 ::std::chrono::nanoseconds(current_time.tv_nsec));
286}
287#endif // __linux__
Austin Schuh858c0212016-11-25 17:23:30 -0800288
brians343bc112013-02-10 01:53:46 +0000289} // namespace aos