You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

uoTimeTest.cpp 7.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. /*********************************************************************
  2. * Software License Agreement (AGPL-3 License)
  3. *
  4. * OpenViBE SDK Test Software
  5. * Based on OpenViBE V1.1.0, Copyright (C) Inria, 2006-2015
  6. * Copyright (C) Inria, 2015-2017,V1.0
  7. *
  8. * This program is free software: you can redistribute it and/or modify
  9. * it under the terms of the GNU Affero General Public License version 3,
  10. * as published by the Free Software Foundation.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU Affero General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Affero General Public License
  18. * along with this program.
  19. * If not, see <http://www.gnu.org/licenses/>.
  20. */
  21. #include <iostream>
  22. #include <limits>
  23. #include <vector>
  24. #include <cmath>
  25. #include <tuple>
  26. #include <chrono>
  27. #include "openvibe/CTime.hpp"
  28. #include "system/ovCTime.h"
  29. #include "ovtAssert.h"
  30. //
  31. // \note This test should be improved. Some improvements could be:
  32. // - Compare clock results to gold standard
  33. // - True testing of monotonic state
  34. // - Stress tests on longer period
  35. //
  36. // \brief Calibrate sleep function to estimate the extra time not spent at sleeping
  37. uint64_t calibrateSleep(const size_t nSample, bool (*sleepFunction)(uint64_t), uint64_t (*timeFunction)())
  38. {
  39. uint64_t maxTime = 0;
  40. for (size_t i = 0; i < nSample; ++i)
  41. {
  42. const uint64_t preTime = timeFunction();
  43. sleepFunction(0);
  44. const uint64_t processingTime = timeFunction() - preTime;
  45. if (processingTime > maxTime) { maxTime = processingTime; }
  46. }
  47. return maxTime;
  48. }
  49. // \brief Record sleep function precision
  50. std::vector<uint64_t> testSleep(const std::vector<uint64_t>& sleepTimes, bool (*sleepFunction)(uint64_t), uint64_t (*timeFunction)())
  51. {
  52. std::vector<uint64_t> effectiveSleepTimes;
  53. for (auto time : sleepTimes)
  54. {
  55. const uint64_t preTime = timeFunction();
  56. sleepFunction(time);
  57. effectiveSleepTimes.push_back(timeFunction() - preTime);
  58. }
  59. return effectiveSleepTimes;
  60. }
  61. // \brief Return a warning count that is incremented when sleep function did not meet following requirements:
  62. // - sleep enough time
  63. // - sleep less than the expected time + delta
  64. size_t assessSleepTestResult(const std::vector<uint64_t>& expected, const std::vector<uint64_t>& result, const uint64_t delta, const uint64_t epsilon)
  65. {
  66. size_t warningCount = 0;
  67. for (size_t i = 0; i < expected.size(); ++i)
  68. {
  69. if (result[i] + epsilon < expected[i] || result[i] > (expected[i] + delta + epsilon))
  70. {
  71. std::cerr << "WARNING: Failure to sleep the right amount of time: [expected|result] = "
  72. << OpenViBE::CTime(expected[i]) << "|" << OpenViBE::CTime(result[i]) << std::endl;
  73. warningCount++;
  74. }
  75. }
  76. return warningCount;
  77. }
  78. // \brief Record clock function data (spin test taken from OpenViBE). Return a tuple with:
  79. // - bool = monotonic state
  80. // - std::vector<uint64_t> = all the cumulative steps
  81. std::tuple<bool, std::vector<uint64_t>> testClock(const uint64_t samplePeriod, const unsigned sampleCountGuess, uint64_t (*timeFunction)())
  82. {
  83. std::vector<uint64_t> cumulativeSteps;
  84. cumulativeSteps.reserve(sampleCountGuess);
  85. bool monotonic = true;
  86. const uint64_t startTime = timeFunction();
  87. uint64_t nowTime = startTime;
  88. uint64_t previousTime = nowTime;
  89. while (nowTime - startTime < samplePeriod)
  90. {
  91. nowTime = timeFunction();
  92. if (nowTime > previousTime) { cumulativeSteps.push_back(nowTime - previousTime); }
  93. else if (nowTime < previousTime)
  94. {
  95. monotonic = false;
  96. break;
  97. }
  98. previousTime = nowTime;
  99. }
  100. return std::make_tuple(monotonic, cumulativeSteps);
  101. }
  102. // \brief Compute jitter measurements for 32:32 time. Return a tuple with:
  103. // - double = mean
  104. // - double = max deviation from mean
  105. // - double = RMSE
  106. std::tuple<double, double, double> assessTimeClock(const std::vector<uint64_t>& measurements)
  107. {
  108. double jitterMax = 0.0;
  109. double jitterMSE = 0.0;
  110. double mean = 0.0;
  111. // compute mean
  112. for (auto& data : measurements)
  113. {
  114. // convert data
  115. auto seconds = data >> 32;
  116. auto microseconds = ((data & 0xFFFFFFFFLL) * 1000000LL) >> 32;
  117. std::chrono::microseconds chronoData = std::chrono::seconds(seconds) + std::chrono::microseconds(microseconds);
  118. mean += double(chronoData.count()) / (1000 * measurements.size());
  119. }
  120. // compute deviations
  121. for (auto& data : measurements)
  122. {
  123. // convert data
  124. auto seconds = data >> 32;
  125. auto microseconds = ((data & 0xFFFFFFFFLL) * 1000000LL) >> 32;
  126. std::chrono::microseconds chronoData = std::chrono::seconds(seconds) + std::chrono::microseconds(microseconds);
  127. const double deviation = std::abs(double(chronoData.count()) / 1000 - mean);
  128. jitterMSE += std::pow(deviation, 2) / measurements.size();
  129. if (deviation - jitterMax > std::numeric_limits<double>::epsilon()) { jitterMax = deviation; }
  130. }
  131. return std::make_tuple(mean, jitterMax, std::sqrt(jitterMSE));
  132. }
  133. int uoTimeTest(int /*argc*/, char* /*argv*/[])
  134. {
  135. // This is a very theoretical test. But if it returns false, we can
  136. // assume that a steady clock is not available on the test
  137. // platform. If it returns true, it just means that the internal clock
  138. // says it is steady...
  139. OVT_ASSERT(System::Time::isClockSteady(), "Failure to retrieve a steady clock");
  140. // Same as above.
  141. // The test is set to 1ms at it is a requirement for OpenViBE clocks
  142. OVT_ASSERT(System::Time::checkResolution(1), "Failure to check for resolution");
  143. // A stress test to check no overflow happens
  144. OVT_ASSERT(System::Time::checkResolution(std::numeric_limits<size_t >::max()), "Failure to check for resolution");
  145. //
  146. // zSleep() function test
  147. //
  148. const std::vector<uint64_t> expectedSleepData = {
  149. 0x80000000LL, 0x40000000LL, 0x20000000LL, 0x10000000LL,
  150. 0x80000000LL, 0x40000000LL, 0x20000000LL, 0x10000000LL,
  151. 0x08000000LL, 0x04000000LL, 0x02000000LL, 0x01000000LL,
  152. 0x08000000LL, 0x04000000LL, 0x02000000LL, 0x01000000LL
  153. };
  154. // calibrate sleep function
  155. const auto deltaTime = calibrateSleep(1000, System::Time::zsleep, System::Time::zgetTime);
  156. std::cout << "INFO: Delta time for zsleep calibration = " << OpenViBE::CTime(deltaTime) << std::endl;
  157. const auto resultSleepData = testSleep(expectedSleepData, System::Time::zsleep, System::Time::zgetTime);
  158. OVT_ASSERT(resultSleepData.size() == expectedSleepData.size(), "Failure to run zsleep tests");
  159. const size_t warningCount = assessSleepTestResult(expectedSleepData, resultSleepData, deltaTime, OpenViBE::CTime(0.005).time());
  160. // relax this threshold in case there is some recurrent problems
  161. // according to the runtime environment
  162. OVT_ASSERT(warningCount <= 2, "Failure to zsleep the right amount of time");
  163. //
  164. // zGetTime() function test
  165. //
  166. // the sample count guess was found in an empiric way
  167. auto resultGetTimeData = testClock(OpenViBE::CTime(0.5).time(), 500000, System::Time::zgetTime);
  168. OVT_ASSERT(std::get<0>(resultGetTimeData), "Failure in zgetTime() test: the clock is not monotonic");
  169. auto clockMetrics = assessTimeClock(std::get<1>(resultGetTimeData));
  170. std::cout << "INFO: Sample count for getTime() = " << std::get<1>(resultGetTimeData).size() << std::endl;
  171. std::cout << "INFO: Mean step in ms for getTime() = " << std::get<0>(clockMetrics) << std::endl;
  172. std::cout << "INFO: Max deviation in ms for getTime() = " << std::get<1>(clockMetrics) << std::endl;
  173. std::cout << "INFO: RMSE in ms for getTime() = " << std::get<2>(clockMetrics) << std::endl;
  174. // We expect at least 1ms resolution
  175. const double resolutionDelta = std::get<0>(clockMetrics) - 1;
  176. OVT_ASSERT(resolutionDelta <= std::numeric_limits<double>::epsilon(), "Failure in zgetTime() test: the clock resolution does not match requirements");
  177. return EXIT_SUCCESS;
  178. }