AP_RTC.cpp 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. #include "AP_RTC.h"
  2. #include <AP_HAL/AP_HAL.h>
  3. #include <AP_Math/AP_Math.h>
  4. #include <GCS_MAVLink/GCS.h>
  5. extern const AP_HAL::HAL& hal;
  6. AP_RTC::AP_RTC()
  7. {
  8. AP_Param::setup_object_defaults(this, var_info);
  9. if (_singleton != nullptr) {
  10. // it's an error to get here. But I don't want to include
  11. // AP_HAL here
  12. return;
  13. }
  14. _singleton = this;
  15. }
  16. // table of user settable parameters
  17. const AP_Param::GroupInfo AP_RTC::var_info[] = {
  18. // @Param: _TYPES
  19. // @DisplayName: Allowed sources of RTC time
  20. // @Description: Specifies which sources of UTC time will be accepted
  21. // @Bitmask: 0:GPS,1:MAVLINK_SYSTEM_TIME,2:HW
  22. // @User: Advanced
  23. AP_GROUPINFO("_TYPES", 1, AP_RTC, allowed_types, 1),
  24. AP_GROUPEND
  25. };
  26. void AP_RTC::set_utc_usec(uint64_t time_utc_usec, source_type type)
  27. {
  28. const uint64_t oldest_acceptable_date = 1546300800000; // 2019-01-01 0:00
  29. if (type >= rtc_source_type) {
  30. // e.g. system-time message when we've been set by the GPS
  31. return;
  32. }
  33. // check it's from an allowed sources:
  34. if (!(allowed_types & (1<<type))) {
  35. return;
  36. }
  37. // don't allow old times
  38. if (time_utc_usec < oldest_acceptable_date) {
  39. return;
  40. }
  41. const uint64_t now = AP_HAL::micros64();
  42. const int64_t tmp = int64_t(time_utc_usec) - int64_t(now);
  43. if (tmp < rtc_shift) {
  44. // can't allow time to go backwards, ever
  45. return;
  46. }
  47. rtc_shift = tmp;
  48. // update hardware clock:
  49. if (type != SOURCE_HW) {
  50. hal.util->set_hw_rtc(time_utc_usec);
  51. }
  52. rtc_source_type = type;
  53. // update signing timestamp
  54. GCS_MAVLINK::update_signing_timestamp(time_utc_usec);
  55. }
  56. bool AP_RTC::get_utc_usec(uint64_t &usec) const
  57. {
  58. if (rtc_source_type == SOURCE_NONE) {
  59. return false;
  60. }
  61. usec = AP_HAL::micros64() + rtc_shift;
  62. return true;
  63. }
  64. bool AP_RTC::get_system_clock_utc(uint8_t &hour, uint8_t &min, uint8_t &sec, uint16_t &ms)
  65. {
  66. // get time of day in ms
  67. uint64_t time_ms = 0;
  68. if (!get_utc_usec(time_ms)) {
  69. return false;
  70. }
  71. time_ms /= 1000U;
  72. // separate time into ms, sec, min, hour and days but all expressed in milliseconds
  73. ms = time_ms % 1000;
  74. uint32_t sec_ms = (time_ms % (60 * 1000)) - ms;
  75. uint32_t min_ms = (time_ms % (60 * 60 * 1000)) - sec_ms - ms;
  76. uint32_t hour_ms = (time_ms % (24 * 60 * 60 * 1000)) - min_ms - sec_ms - ms;
  77. // convert times as milliseconds into appropriate units
  78. sec = sec_ms / 1000;
  79. min = min_ms / (60 * 1000);
  80. hour = hour_ms / (60 * 60 * 1000);
  81. return true;
  82. }
  83. // get milliseconds from now to a target time of day expressed as
  84. // hour, min, sec, ms. Match starts from first value that is not
  85. // -1. I.e. specifying hour=-1, minutes=10 will ignore the hour and
  86. // return time until 10 minutes past 12am (utc) NOTE: if this time has
  87. // just past then you can expect a return value of roughly 86340000 -
  88. // the number of milliseconds in a day.
  89. uint32_t AP_RTC::get_time_utc(int32_t hour, int32_t min, int32_t sec, int32_t ms)
  90. {
  91. // determine highest value specified (0=none, 1=ms, 2=sec, 3=min, 4=hour)
  92. int8_t largest_element = 0;
  93. if (hour != -1) {
  94. largest_element = 4;
  95. } else if (min != -1) {
  96. largest_element = 3;
  97. } else if (sec != -1) {
  98. largest_element = 2;
  99. } else if (ms != -1) {
  100. largest_element = 1;
  101. } else {
  102. // exit immediately if no time specified
  103. return 0;
  104. }
  105. // get start_time_ms as h, m, s, ms
  106. uint8_t curr_hour, curr_min, curr_sec;
  107. uint16_t curr_ms;
  108. if (!get_system_clock_utc(curr_hour, curr_min, curr_sec, curr_ms)) {
  109. return 0;
  110. }
  111. int32_t total_delay_ms = 0;
  112. // calculate ms to target
  113. if (largest_element >= 1) {
  114. total_delay_ms += ms - curr_ms;
  115. }
  116. if (largest_element == 1 && total_delay_ms < 0) {
  117. return static_cast<uint32_t>(total_delay_ms += 1000);
  118. }
  119. // calculate sec to target
  120. if (largest_element >= 2) {
  121. total_delay_ms += (sec - curr_sec)*1000;
  122. }
  123. if (largest_element == 2 && total_delay_ms < 0) {
  124. return static_cast<uint32_t>(total_delay_ms += (60*1000));
  125. }
  126. // calculate min to target
  127. if (largest_element >= 3) {
  128. total_delay_ms += (min - curr_min)*60*1000;
  129. }
  130. if (largest_element == 3 && total_delay_ms < 0) {
  131. return static_cast<uint32_t>(total_delay_ms += (60*60*1000));
  132. }
  133. // calculate hours to target
  134. if (largest_element >= 4) {
  135. total_delay_ms += (hour - curr_hour)*60*60*1000;
  136. }
  137. if (largest_element == 4 && total_delay_ms < 0) {
  138. return static_cast<uint32_t>(total_delay_ms += (24*60*60*1000));
  139. }
  140. // total delay in milliseconds
  141. return static_cast<uint32_t>(total_delay_ms);
  142. }
  143. // singleton instance
  144. AP_RTC *AP_RTC::_singleton;
  145. namespace AP {
  146. AP_RTC &rtc()
  147. {
  148. return *AP_RTC::get_singleton();
  149. }
  150. }