diff --git a/src/mcp2515/mcp2515.c b/src/mcp2515/mcp2515.c index 376077f1..4c2531a1 100644 --- a/src/mcp2515/mcp2515.c +++ b/src/mcp2515/mcp2515.c @@ -456,8 +456,7 @@ upm_result_t mcp2515_set_opmode(const mcp2515_context dev, << _MCP2515_CANSTAT_OPMODE_SHIFT); bool done = false; - upm_clock_t clock; - upm_clock_init(&clock); + upm_clock_t clock = upm_clock_init(); do { @@ -623,8 +622,7 @@ upm_result_t mcp2515_transmit_buffer(const mcp2515_context dev, return UPM_SUCCESS; // now spin with timeout waiting for it to be transmitted - upm_clock_t clock; - upm_clock_init(&clock); + upm_clock_t clock = upm_clock_init(); bool done = false; do diff --git a/src/ppd42ns/ppd42ns.c b/src/ppd42ns/ppd42ns.c index c26fb1d6..360e3ffe 100644 --- a/src/ppd42ns/ppd42ns.c +++ b/src/ppd42ns/ppd42ns.c @@ -99,7 +99,7 @@ ppd42ns_dust_data ppd42ns_get_data(const ppd42ns_context dev) unsigned int low_pulse_occupancy = 0; - upm_clock_init(&max_loop_time); + max_loop_time = upm_clock_init(); do { low_pulse_occupancy += ppd42ns_pulse_in(dev, 0); @@ -132,11 +132,10 @@ static uint32_t ppd42ns_pulse_in(const ppd42ns_context dev, assert(dev != NULL); // we run for no more than 1 second at a time - upm_clock_t max_time; upm_clock_t pulse_time; uint32_t total_pulse_time = 0; - upm_clock_init(&max_time); + upm_clock_t max_time = upm_clock_init(); bool pin_level; bool is_timing = false; @@ -146,7 +145,7 @@ static uint32_t ppd42ns_pulse_in(const ppd42ns_context dev, if (!is_timing && pin_level == high_low_value) { // level is desired level, but not currently timing - upm_clock_init(&pulse_time); + pulse_time = upm_clock_init(); is_timing = true; } else if (is_timing && pin_level != high_low_value) diff --git a/src/rn2903/rn2903.c b/src/rn2903/rn2903.c index 01a011da..20239ba1 100644 --- a/src/rn2903/rn2903.c +++ b/src/rn2903/rn2903.c @@ -317,8 +317,7 @@ RN2903_RESPONSE_T rn2903_waitfor_response(const rn2903_context dev, memset(dev->resp_data, 0, RN2903_MAX_BUFFER); dev->resp_len = 0; - upm_clock_t clock; - upm_clock_init(&clock); + upm_clock_t clock = upm_clock_init(); uint32_t elapsed = 0; do diff --git a/src/speaker/speaker.c b/src/speaker/speaker.c index bd792c6d..63bf2a82 100644 --- a/src/speaker/speaker.c +++ b/src/speaker/speaker.c @@ -206,8 +206,7 @@ upm_result_t speaker_emit(const speaker_context dev, unsigned int freq, if (speaker_set_frequency(dev, freq)) return UPM_ERROR_OPERATION_FAILED; - upm_clock_t clock; - upm_clock_init(&clock); + upm_clock_t clock = upm_clock_init(); mraa_pwm_enable(dev->pwm, 1); while (upm_elapsed_ms(&clock) < emit_ms) diff --git a/src/uartat/uartat.c b/src/uartat/uartat.c index 51516d62..f3ab0520 100644 --- a/src/uartat/uartat.c +++ b/src/uartat/uartat.c @@ -265,8 +265,7 @@ int uartat_command_with_response(const uartat_context dev, { memset(resp, 0, resp_len); - upm_clock_t clock; - upm_clock_init(&clock); + upm_clock_t clock = upm_clock_init(); size_t idx = 0; @@ -320,8 +319,7 @@ bool uartat_command_waitfor(const uartat_context dev, const char *cmd, memset(resp, 0, resp_len); - upm_clock_t clock; - upm_clock_init(&clock); + upm_clock_t clock = upm_clock_init(); size_t idx = 0; diff --git a/src/utilities/upm_utilities.c b/src/utilities/upm_utilities.c index 39e0a123..850223f8 100644 --- a/src/utilities/upm_utilities.c +++ b/src/utilities/upm_utilities.c @@ -26,31 +26,42 @@ */ #ifndef _POSIX_C_SOURCE -// We need at least 199309L for nanosleep() +// We need at least 199309L for clock_nanosleep() # define _POSIX_C_SOURCE 200809L #endif +#include #include #include #include "upm_platform.h" #include "upm_utilities.h" -// https://www3.epa.gov/airnow/aqi-technical-assistance-document-may2016.pdf -static struct aqi { - float clow; - float chigh; - int llow; - int lhigh; -} aqi[] = { - {0.0, 12.0, 0, 50}, - {12.1, 35.4, 51, 100}, - {35.5, 55.4, 101, 150}, - {55.5, 150.4, 151, 200}, - {150.5, 250.4, 201, 300}, - {250.5, 350.4, 301, 400}, - {350.5, 500.4, 401, 500}, -}; -void upm_delay(unsigned int time) +/** + * Calculate the delta of two upm_clock_t values as + * delta = finish - start + * + * @param finish Ending upm_clock_t time + * @param start Beginning upm_clock_t time + * @return Time in nanoseconds + */ +static uint64_t _delta_ns(const upm_clock_t* finish, const upm_clock_t* start) +{ + uint64_t delta; + assert((finish != NULL) && (start != NULL) && "_delta_ns, arguments cannot be NULL"); + +#if defined(UPM_PLATFORM_ZEPHYR) + delta = SYS_CLOCK_HW_CYCLES_TO_NS64(*finish - *start); +#elif defined(UPM_PLATFORM_LINUX) + + delta = (finish->tv_sec * 1000000000UL + finish->tv_nsec) - + (start->tv_sec * 1000000000UL + start->tv_nsec); +#else +#error "Unknown platform, valid platforms are {UPM_PLATFORM_ZEPHYR, UPM_PLATFORM_LINUX}" +#endif + return delta; +} + +void upm_delay(uint32_t time) { /* Return if time == 0 */ if (!time) @@ -58,17 +69,11 @@ void upm_delay(unsigned int time) #if defined(UPM_PLATFORM_LINUX) - struct timespec delay_time; - - delay_time.tv_sec = time; - delay_time.tv_nsec = 0; - // The advantage over sleep(3) here is that it will not use - // an alarm signal or handler. + upm_clock_t delay_time = {time, 0}; // here we spin until the delay is complete - detecting signals // and continuing where we left off - while (nanosleep(&delay_time, &delay_time) && errno == EINTR) - ; // loop + while (clock_nanosleep(CLOCK_MONOTONIC, 0, &delay_time, &delay_time) == EINTR); #elif defined(UPM_PLATFORM_ZEPHYR) # if KERNEL_VERSION_MAJOR == 1 && KERNEL_VERSION_MINOR >= 6 @@ -88,10 +93,12 @@ void upm_delay(unsigned int time) # endif +#else +#error "Unknown platform, valid platforms are {UPM_PLATFORM_ZEPHYR, UPM_PLATFORM_LINUX}" #endif } -void upm_delay_ms(unsigned int time) +void upm_delay_ms(uint32_t time) { /* Return if time == 0 */ if (!time) @@ -99,14 +106,11 @@ void upm_delay_ms(unsigned int time) #if defined(UPM_PLATFORM_LINUX) - struct timespec delay_time; + upm_clock_t delay_time = {time / 1000, (time % 1000) * 1000000UL}; - delay_time.tv_sec = time / 1000; - delay_time.tv_nsec = (time % 1000) * 1000000; // here we spin until the delay is complete - detecting signals // and continuing where we left off - while (nanosleep(&delay_time, &delay_time) && errno == EINTR) - ; // loop + while (clock_nanosleep(CLOCK_MONOTONIC, 0, &delay_time, &delay_time) == EINTR); #elif defined(UPM_PLATFORM_ZEPHYR) # if KERNEL_VERSION_MAJOR == 1 && KERNEL_VERSION_MINOR >= 6 @@ -125,10 +129,12 @@ void upm_delay_ms(unsigned int time) nano_timer_test(&timer, TICKS_UNLIMITED); # endif +#else +#error "Unknown platform, valid platforms are {UPM_PLATFORM_ZEPHYR, UPM_PLATFORM_LINUX}" #endif } -void upm_delay_us(unsigned int time) +void upm_delay_us(uint32_t time) { /* Return if time == 0 */ if (!time) @@ -136,24 +142,19 @@ void upm_delay_us(unsigned int time) #if defined(UPM_PLATFORM_LINUX) - struct timespec delay_time; + upm_clock_t delay_time = {time / 1000000, (time % 1000000) * 1000}; - delay_time.tv_sec = time / 1000000; - delay_time.tv_nsec = (time % 1000000) * 1000; // here we spin until the delay is complete - detecting signals // and continuing where we left off - while (nanosleep(&delay_time, &delay_time) && errno == EINTR) - ; // loop + while (clock_nanosleep(CLOCK_MONOTONIC, 0, &delay_time, &delay_time) == EINTR); #elif defined(UPM_PLATFORM_ZEPHYR) # if KERNEL_VERSION_MAJOR == 1 && KERNEL_VERSION_MINOR >= 6 // we will use a upm_clock to do microsecond timings here as k_timer has // only a millisecond resolution. So we init a clock and spin. - upm_clock_t timer; - upm_clock_init(&timer); - while (upm_elapsed_us(&timer) < time) - ; // spin + upm_clock_t timer = upm_clock_init(); + while (upm_elapsed_us(&timer) < time); // spin # else @@ -165,109 +166,129 @@ void upm_delay_us(unsigned int time) # endif +#else +#error "Unknown platform, valid platforms are {UPM_PLATFORM_ZEPHYR, UPM_PLATFORM_LINUX}" #endif } -void upm_clock_init(upm_clock_t *clock) +void upm_delay_ns(uint64_t time) { + /* Return if time == 0 */ + if (!time) + return; + #if defined(UPM_PLATFORM_LINUX) - gettimeofday(clock, NULL); + upm_clock_t delay_time = {time / 1000000000UL, time % 1000000000UL}; + + // here we spin until the delay is complete - detecting signals + // and continuing where we left off + while (clock_nanosleep(CLOCK_MONOTONIC, 0, &delay_time, &delay_time) == EINTR); #elif defined(UPM_PLATFORM_ZEPHYR) - *clock = sys_cycle_get_32(); +# if KERNEL_VERSION_MAJOR == 1 && KERNEL_VERSION_MINOR >= 6 + // we will use a upm_clock to do microsecond timings here as k_timer has + // only a millisecond resolution. So we init a clock and spin. + + upm_clock_t timer = upm_clock_init(); + while (upm_elapsed_ns(&timer) < time); // spin + +# else + + struct nano_timer timer; + void *timer_data[1]; + nano_timer_init(&timer, timer_data); + nano_timer_start(&timer, time + 1); + nano_timer_test(&timer, TICKS_UNLIMITED); + +# endif + +#else +#error "Unknown platform, valid platforms are {UPM_PLATFORM_ZEPHYR, UPM_PLATFORM_LINUX}" #endif } -uint32_t upm_elapsed_ms(upm_clock_t *clock) +upm_clock_t upm_clock_init(void) { + upm_clock_t clock = {0}; #if defined(UPM_PLATFORM_LINUX) - - struct timeval elapsed, now; - uint32_t elapse; - - // get current time - gettimeofday(&now, NULL); - - struct timeval startTime = *clock; - - // compute the delta since startTime - if( (elapsed.tv_usec = now.tv_usec - startTime.tv_usec) < 0 ) - { - elapsed.tv_usec += 1000000; - elapsed.tv_sec = now.tv_sec - startTime.tv_sec - 1; - } - else - { - elapsed.tv_sec = now.tv_sec - startTime.tv_sec; - } - - elapse = (uint32_t)((elapsed.tv_sec * 1000) + (elapsed.tv_usec / 1000)); - - // never return 0 - if (elapse == 0) - elapse = 1; - - return elapse; - + clock_gettime(CLOCK_MONOTONIC, &clock); #elif defined(UPM_PLATFORM_ZEPHYR) - uint32_t now = sys_cycle_get_32(); - - uint32_t elapsed = - (uint32_t)(SYS_CLOCK_HW_CYCLES_TO_NS64(now - *clock)/(uint64_t)1000000); - - if (elapsed == 0) - elapsed = 1; - - return elapsed; + clock = sys_cycle_get_32(); +#else +#error "Unknown platform, valid platforms are {UPM_PLATFORM_ZEPHYR, UPM_PLATFORM_LINUX}" #endif + + return clock; } -uint32_t upm_elapsed_us(upm_clock_t *clock) +uint64_t upm_elapsed_ms(const upm_clock_t *clock) { + assert((clock != NULL) && "upm_elapsed_ms, clock cannot be NULL"); + + upm_clock_t now = {0}; + #if defined(UPM_PLATFORM_LINUX) - - struct timeval elapsed, now; - uint32_t elapse; - - // get current time - gettimeofday(&now, NULL); - - struct timeval startTime = *clock; - - // compute the delta since startTime - if( (elapsed.tv_usec = now.tv_usec - startTime.tv_usec) < 0 ) - { - elapsed.tv_usec += 1000000; - elapsed.tv_sec = now.tv_sec - startTime.tv_sec - 1; - } - else - { - elapsed.tv_sec = now.tv_sec - startTime.tv_sec; - } - - elapse = (uint32_t)((elapsed.tv_sec * 1000000) + elapsed.tv_usec); - - // never return 0 - if (elapse == 0) - elapse = 1; - - return elapse; - + clock_gettime(CLOCK_MONOTONIC, &now); #elif defined(UPM_PLATFORM_ZEPHYR) - uint32_t now = sys_cycle_get_32(); - - uint32_t elapsed = - (uint32_t)(SYS_CLOCK_HW_CYCLES_TO_NS64(now - *clock)/(uint64_t)1000); - - // never return 0 - if (elapsed == 0) - elapsed = 1; - - return elapsed; + now = sys_cycle_get_32(); +#else +#error "Unknown platform, valid platforms are {UPM_PLATFORM_ZEPHYR, UPM_PLATFORM_LINUX}" #endif + + return _delta_ns(&now, clock)/1000000; } +uint64_t upm_elapsed_us(const upm_clock_t *clock) +{ + assert((clock != NULL) && "upm_elapsed_us, clock cannot be NULL"); + + upm_clock_t now = {0}; + +#if defined(UPM_PLATFORM_LINUX) + clock_gettime(CLOCK_MONOTONIC, &now); +#elif defined(UPM_PLATFORM_ZEPHYR) + now = sys_cycle_get_32(); +#else +#error "Unknown platform, valid platforms are {UPM_PLATFORM_ZEPHYR, UPM_PLATFORM_LINUX}" +#endif + + return _delta_ns(&now, clock)/1000; +} + +uint64_t upm_elapsed_ns(const upm_clock_t *clock) +{ + assert((clock != NULL) && "upm_elapsed_ns, clock cannot be NULL"); + + upm_clock_t now = {0}; + +#if defined(UPM_PLATFORM_LINUX) + clock_gettime(CLOCK_MONOTONIC, &now); +#elif defined(UPM_PLATFORM_ZEPHYR) + now = sys_cycle_get_32(); +#else +#error "Unknown platform, valid platforms are {UPM_PLATFORM_ZEPHYR, UPM_PLATFORM_LINUX}" +#endif + + return _delta_ns(&now, clock); +} + +// https://www3.epa.gov/airnow/aqi-technical-assistance-document-may2016.pdf +static struct aqi { + float clow; + float chigh; + int llow; + int lhigh; +} aqi[] = { + {0.0, 12.0, 0, 50}, + {12.1, 35.4, 51, 100}, + {35.5, 55.4, 101, 150}, + {55.5, 150.4, 151, 200}, + {150.5, 250.4, 201, 300}, + {250.5, 350.4, 301, 400}, + {350.5, 500.4, 401, 500}, +}; + int upm_ugm3_to_aqi (double ugm3) { int i; diff --git a/src/utilities/upm_utilities.h b/src/utilities/upm_utilities.h index eefc5827..1910ee81 100644 --- a/src/utilities/upm_utilities.h +++ b/src/utilities/upm_utilities.h @@ -40,7 +40,7 @@ extern "C" { #include #include -typedef struct timeval upm_clock_t; +typedef struct timespec upm_clock_t; #endif /* UPM_PLATFORM_LINUX */ #if defined(UPM_PLATFORM_ZEPHYR) @@ -58,38 +58,55 @@ typedef struct timeval upm_clock_t; #define PRINT printk #endif -typedef uint32_t upm_clock_t; +typedef uint64_t upm_clock_t; #endif /* UPM_PLATFORM_ZEPHYR */ /** - * Delay for a number of seconds + * Delay for a number of seconds (s) * * @param time The number of seconds to delay for */ -void upm_delay(unsigned int time); +void upm_delay(uint32_t time); /** - * Delay for a number of milliseconds + * Delay for a number of milliseconds (ms) * * @param time The number of milliseconds to delay for */ -void upm_delay_ms(unsigned int time); +void upm_delay_ms(uint32_t time); /** - * Delay for a number of microseconds + * Delay for a number of microseconds (us) * * @param time The number of microseconds to delay for */ -void upm_delay_us(unsigned int time); +void upm_delay_us(uint32_t time); + +/** + * Delay for a number of nanoseconds (ns) + * + * Note, sub-microsecond accurate time on *nix is generally not available OOB + * and high resolution times are also not supported on all HW architectures. + * + * @param time The number of nanoseconds to delay for + */ +void upm_delay_ns(uint64_t time); /** * Initialize a clock. This can be used with upm_elapsed_ms() and * upm_elapsed_us() for measuring a duration. * - * @param clock The upm_clock_t to initialize to the current time + * For *nix operating systems, this initializes a MONOTONIC clock. + * + * Example: + * upm_clock_t start = upm_clock_init(); + * ... do stuff ... + * uint64_t delta_ns = upm_elapsed_us(&start); + * + * @return The upm_clock_t initialized to the current time */ -void upm_clock_init(upm_clock_t *clock); +upm_clock_t upm_clock_init(void); /** * Return the elapsed time in milliseconds since upm_init_clock() was @@ -99,7 +116,7 @@ void upm_clock_init(upm_clock_t *clock); * @return the number of milliseconds elapsed since upm_init_clock() * was called on the clock parameter. */ -uint32_t upm_elapsed_ms(upm_clock_t *clock); +uint64_t upm_elapsed_ms(const upm_clock_t *clock); /** * Return the elapsed time in microseconds since upm_init_clock() was @@ -109,7 +126,20 @@ uint32_t upm_elapsed_ms(upm_clock_t *clock); * @return the number of microseconds elapsed since upm_init_clock() * was called on the clock parameter. */ -uint32_t upm_elapsed_us(upm_clock_t *clock); +uint64_t upm_elapsed_us(const upm_clock_t *clock); + +/** + * Return the elapsed time in nanoseconds since upm_init_clock() was + * last called. + * + * Note, sub-microsecond accurate time on *nix is generally not available OOB + * and high resolution times are also not supported on all HW architectures. + * + * @param clock A upm_clock_t initialized by upm_init_clock() + * @return the number of nanoseconds elapsed since upm_init_clock() + * was called on the clock parameter. + */ +uint64_t upm_elapsed_ns(const upm_clock_t *clock); /** * Return the AQI (based on EPA standards) using the ugm3 value diff --git a/src/wfs/wfs.c b/src/wfs/wfs.c index 7e6fe3a0..fe51865e 100644 --- a/src/wfs/wfs.c +++ b/src/wfs/wfs.c @@ -88,7 +88,7 @@ void wfs_init_clock(const wfs_context dev) { assert(dev != NULL); - upm_clock_init(&dev->clock); + dev->clock = upm_clock_init(); } uint32_t wfs_get_millis(const wfs_context dev) diff --git a/src/zfm20/zfm20.cxx b/src/zfm20/zfm20.cxx index 021cef04..888685e8 100644 --- a/src/zfm20/zfm20.cxx +++ b/src/zfm20/zfm20.cxx @@ -131,7 +131,7 @@ int ZFM20::writeCmdPacket(uint8_t *pkt, int len) void ZFM20::initClock() { - upm_clock_init(&m_clock); + m_clock = upm_clock_init(); } uint32_t ZFM20::getMillis() diff --git a/tests/unit/utilities/utilities_tests.cxx b/tests/unit/utilities/utilities_tests.cxx index dba0063e..2fd66bb5 100644 --- a/tests/unit/utilities/utilities_tests.cxx +++ b/tests/unit/utilities/utilities_tests.cxx @@ -22,11 +22,29 @@ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#include +#include #include "gtest/gtest.h" #include "upm_utilities.h" #include "upm_utilities.hpp" +/* Average over AVG_CNT iterations */ +#define AVG_CNT 5 + +/* Specify a delay for all tests under 1s */ +#define ms_50 std::chrono::milliseconds(50) + +/* Specify a +/- value for all tests under 1s. Since the delay methods in + * non-realtime operating systems can vary greatly, use a lenient value for + * testing these methods. */ +#define time_range std::chrono::milliseconds(5) + +/* Helper defines */ +#define to_ms std::chrono::duration_cast +#define to_us std::chrono::duration_cast +#define to_ns std::chrono::duration_cast + /* Utilities test fixture */ class utilities_unit : public ::testing::Test { @@ -44,44 +62,107 @@ class utilities_unit : public ::testing::Test virtual void TearDown() {} }; +/* Sanity check on min_delay_ns */ +TEST_F(utilities_unit, min_delay_LT_500us) +{ + /* Determine a rough-average for the minimum delay using chrono */ + std::chrono::nanoseconds min_delay_ns = std::chrono::nanoseconds::zero(); + for (int i = 0; i < AVG_CNT; i++) + { + auto start = std::chrono::steady_clock::now(); + std::this_thread::sleep_for(std::chrono::nanoseconds(1)); + auto end = std::chrono::steady_clock::now(); + min_delay_ns += to_ns(end-start); + } + min_delay_ns /= AVG_CNT; + + ASSERT_LT(to_us(min_delay_ns).count(), 500); +} + /* Test the second delay method */ TEST_F(utilities_unit, test_upm_delay) { - upm_clock_t clock; - upm_clock_init(&clock); + upm_clock_t clock = upm_clock_init(); /* Test a corner case */ upm_delay(0); + + /* +- check for 0s */ + EXPECT_EQ(upm_elapsed_ms(&clock), 0); + + clock = upm_clock_init(); upm_delay(1); - /* +- check for 1s +/- 1ms */ - ASSERT_NEAR(upm_elapsed_ms(&clock), 1000, 1); + /* +- check near 1s */ + EXPECT_NEAR(upm_elapsed_ms(&clock), 1000, time_range.count()); } /* Test the millisecond delay method */ TEST_F(utilities_unit, test_upm_delay_ms) { - upm_clock_t clock; - upm_clock_init(&clock); + upm_clock_t clock = upm_clock_init(); /* Test a corner case */ upm_delay_ms(0); - upm_delay_ms(50); - /* +- check for 50ms +/- 1ms */ - ASSERT_NEAR(upm_elapsed_ms(&clock), 50, 1); + /* +- check for 0ms */ + EXPECT_EQ(upm_elapsed_ms(&clock), 0); + + clock = upm_clock_init(); + upm_delay_ms(ms_50.count() * AVG_CNT); + + /* +- check near 50ms */ + EXPECT_NEAR(upm_elapsed_ms(&clock)/AVG_CNT, ms_50.count(), time_range.count()); } /* Test the microsecond delay method */ TEST_F(utilities_unit, test_upm_delay_us) { - upm_clock_t clock; - upm_clock_init(&clock); + upm_clock_t clock = upm_clock_init(); /* Test a corner case */ upm_delay_us(0); - upm_delay_us(1000); - /* +- check for 1000us +/- 150us */ - ASSERT_NEAR(upm_elapsed_us(&clock), 1000, 150); + /* +- check for 0us +/- 100us */ + EXPECT_NEAR(upm_elapsed_us(&clock), 0, 100); + + clock = upm_clock_init(); + upm_delay_us(to_us(ms_50).count() * AVG_CNT); + + /* +- check near 50ms */ + EXPECT_NEAR(upm_elapsed_us(&clock)/AVG_CNT, to_us(ms_50).count(), + to_us(time_range).count()); +} + +/* Test the nanosecond delay method */ +TEST_F(utilities_unit, test_upm_delay_ns) +{ + upm_clock_t clock = upm_clock_init(); + + /* Test a corner case */ + upm_delay_ns(0); + + /* +- check for 0us +/- 100us */ + EXPECT_NEAR(upm_elapsed_ns(&clock), 0, 100000); + + clock = upm_clock_init(); + upm_delay_ns(to_ns(ms_50).count() * AVG_CNT); + + /* +- check near 50ms */ + EXPECT_NEAR(upm_elapsed_ns(&clock)/AVG_CNT, to_ns(ms_50).count(), + to_ns(time_range).count()); +} + +/* Test the max us delay (default to disabled) */ +TEST_F(utilities_unit, DISABLED_test_upm_delay_us_max) +{ + upm_clock_t clock = upm_clock_init(); + upm_delay_us(4294967295); + EXPECT_NEAR(upm_elapsed_us(&clock), 4294967295, 150); +} + +/* Test the Air Quality Index method */ +TEST_F(utilities_unit, test_upm_ugm3_to_aqi) +{ + EXPECT_EQ(upm_ugm3_to_aqi(10), 41); }