clock.hpp 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. /*
  2. * Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
  3. */
  4. #pragma once
  5. #include <cassert>
  6. #include <ctime>
  7. #include <cstdint>
  8. #include <unistd.h>
  9. #include <sys/time.h>
  10. #include <sys/types.h>
  11. #include <uavcan/driver/system_clock.hpp>
  12. #include <uavcan_linux/exception.hpp>
  13. namespace uavcan_linux
  14. {
  15. /**
  16. * Different adjustment modes can be used for time synchronization
  17. */
  18. enum class ClockAdjustmentMode
  19. {
  20. SystemWide, ///< Adjust the clock globally for the whole system; requires root privileges
  21. PerDriverPrivate ///< Adjust the clock only for the current driver instance
  22. };
  23. /**
  24. * Linux system clock driver.
  25. * Requires librt.
  26. */
  27. class SystemClock : public uavcan::ISystemClock
  28. {
  29. uavcan::UtcDuration private_adj_;
  30. uavcan::UtcDuration gradual_adj_limit_;
  31. const ClockAdjustmentMode adj_mode_;
  32. std::uint64_t step_adj_cnt_;
  33. std::uint64_t gradual_adj_cnt_;
  34. static constexpr std::int64_t Int1e6 = 1000000;
  35. static constexpr std::uint64_t UInt1e6 = 1000000;
  36. bool performStepAdjustment(const uavcan::UtcDuration adjustment)
  37. {
  38. step_adj_cnt_++;
  39. const std::int64_t usec = adjustment.toUSec();
  40. timeval tv;
  41. if (gettimeofday(&tv, NULL) != 0)
  42. {
  43. return false;
  44. }
  45. tv.tv_sec += usec / Int1e6;
  46. tv.tv_usec += usec % Int1e6;
  47. return settimeofday(&tv, nullptr) == 0;
  48. }
  49. bool performGradualAdjustment(const uavcan::UtcDuration adjustment)
  50. {
  51. gradual_adj_cnt_++;
  52. const std::int64_t usec = adjustment.toUSec();
  53. timeval tv;
  54. tv.tv_sec = usec / Int1e6;
  55. tv.tv_usec = usec % Int1e6;
  56. return adjtime(&tv, nullptr) == 0;
  57. }
  58. public:
  59. /**
  60. * By default, the clock adjustment mode will be selected automatically - global if root, private otherwise.
  61. */
  62. explicit SystemClock(ClockAdjustmentMode adj_mode = detectPreferredClockAdjustmentMode())
  63. : gradual_adj_limit_(uavcan::UtcDuration::fromMSec(4000))
  64. , adj_mode_(adj_mode)
  65. , step_adj_cnt_(0)
  66. , gradual_adj_cnt_(0)
  67. { }
  68. /**
  69. * Returns monotonic timestamp from librt.
  70. * @throws uavcan_linux::Exception.
  71. */
  72. uavcan::MonotonicTime getMonotonic() const override
  73. {
  74. timespec ts;
  75. if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0)
  76. {
  77. throw Exception("Failed to get monotonic time");
  78. }
  79. return uavcan::MonotonicTime::fromUSec(std::uint64_t(ts.tv_sec) * UInt1e6 + ts.tv_nsec / 1000);
  80. }
  81. /**
  82. * Returns wall time from gettimeofday().
  83. * @throws uavcan_linux::Exception.
  84. */
  85. uavcan::UtcTime getUtc() const override
  86. {
  87. timeval tv;
  88. if (gettimeofday(&tv, NULL) != 0)
  89. {
  90. throw Exception("Failed to get UTC time");
  91. }
  92. uavcan::UtcTime utc = uavcan::UtcTime::fromUSec(std::uint64_t(tv.tv_sec) * UInt1e6 + tv.tv_usec);
  93. if (adj_mode_ == ClockAdjustmentMode::PerDriverPrivate)
  94. {
  95. utc += private_adj_;
  96. }
  97. return utc;
  98. }
  99. /**
  100. * Adjusts the wall clock.
  101. * Behavior depends on the selected clock adjustment mode - @ref ClockAdjustmentMode.
  102. * Clock adjustment mode can be set only once via constructor.
  103. *
  104. * If the system wide adjustment mode is selected, two ways for performing adjustment exist:
  105. * - Gradual adjustment using adjtime(), if the phase error is less than gradual adjustment limit.
  106. * - Step adjustment using settimeofday(), if the phase error is above gradual adjustment limit.
  107. * The gradual adjustment limit can be configured at any time via the setter method.
  108. *
  109. * @throws uavcan_linux::Exception.
  110. */
  111. void adjustUtc(const uavcan::UtcDuration adjustment) override
  112. {
  113. if (adj_mode_ == ClockAdjustmentMode::PerDriverPrivate)
  114. {
  115. private_adj_ += adjustment;
  116. }
  117. else
  118. {
  119. assert(private_adj_.isZero());
  120. assert(!gradual_adj_limit_.isNegative());
  121. bool success = false;
  122. if (adjustment.getAbs() < gradual_adj_limit_)
  123. {
  124. success = performGradualAdjustment(adjustment);
  125. }
  126. else
  127. {
  128. success = performStepAdjustment(adjustment);
  129. }
  130. if (!success)
  131. {
  132. throw Exception("Clock adjustment failed");
  133. }
  134. }
  135. }
  136. /**
  137. * Sets the maximum phase error to use adjtime().
  138. * If the phase error exceeds this value, settimeofday() will be used instead.
  139. */
  140. void setGradualAdjustmentLimit(uavcan::UtcDuration limit)
  141. {
  142. if (limit.isNegative())
  143. {
  144. limit = uavcan::UtcDuration();
  145. }
  146. gradual_adj_limit_ = limit;
  147. }
  148. uavcan::UtcDuration getGradualAdjustmentLimit() const { return gradual_adj_limit_; }
  149. ClockAdjustmentMode getAdjustmentMode() const { return adj_mode_; }
  150. /**
  151. * This is only applicable if the selected clock adjustment mode is private.
  152. * In system wide mode this method will always return zero duration.
  153. */
  154. uavcan::UtcDuration getPrivateAdjustment() const { return private_adj_; }
  155. /**
  156. * Statistics that allows to evaluate clock sync preformance.
  157. */
  158. std::uint64_t getStepAdjustmentCount() const { return step_adj_cnt_; }
  159. std::uint64_t getGradualAdjustmentCount() const { return gradual_adj_cnt_; }
  160. std::uint64_t getAdjustmentCount() const
  161. {
  162. return getStepAdjustmentCount() + getGradualAdjustmentCount();
  163. }
  164. /**
  165. * This static method decides what is the optimal clock sync adjustment mode for the current configuration.
  166. * It selects system wide mode if the application is running as root; otherwise it prefers
  167. * the private adjustment mode because the system wide mode requires root privileges.
  168. */
  169. static ClockAdjustmentMode detectPreferredClockAdjustmentMode()
  170. {
  171. const bool godmode = geteuid() == 0;
  172. return godmode ? ClockAdjustmentMode::SystemWide : ClockAdjustmentMode::PerDriverPrivate;
  173. }
  174. };
  175. }