Merge pull request 'Added an additional conversion function' (#584) from eive/fsfw:mueller/clock-addition into development
Reviewed-on: fsfw/fsfw#584
This commit is contained in:
commit
b8516b15cb
@ -28,6 +28,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
creation call. It allows passing context information and an arbitrary user argument into
|
creation call. It allows passing context information and an arbitrary user argument into
|
||||||
the message queue. Also streamlined and simplified `MessageQueue` implementation for all OSALs
|
the message queue. Also streamlined and simplified `MessageQueue` implementation for all OSALs
|
||||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/583
|
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/583
|
||||||
|
- Clock:
|
||||||
|
- `timeval` to `TimeOfDay_t`
|
||||||
|
- Added Mutex for gmtime calls: (compare http://www.opengate.at/blog/2020/01/timeless/)
|
||||||
|
- Moved the statics used by Clock in ClockCommon.cpp to this file
|
||||||
|
- Better check for leap seconds
|
||||||
|
- Added Unittests for Clock (only getter)
|
||||||
|
|
||||||
## Removed
|
## Removed
|
||||||
|
|
||||||
|
@ -11,9 +11,6 @@
|
|||||||
// TODO sanitize input?
|
// TODO sanitize input?
|
||||||
// TODO much of this code can be reused for tick-only systems
|
// TODO much of this code can be reused for tick-only systems
|
||||||
|
|
||||||
uint16_t Clock::leapSeconds = 0;
|
|
||||||
MutexIF* Clock::timeMutex = nullptr;
|
|
||||||
|
|
||||||
uint32_t Clock::getTicksPerSecond(void) { return 1000; }
|
uint32_t Clock::getTicksPerSecond(void) { return 1000; }
|
||||||
|
|
||||||
ReturnValue_t Clock::setClock(const TimeOfDay_t* time) {
|
ReturnValue_t Clock::setClock(const TimeOfDay_t* time) {
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
|
||||||
|
#include "fsfw/ipc/MutexGuard.h"
|
||||||
#include "fsfw/platform.h"
|
#include "fsfw/platform.h"
|
||||||
#include "fsfw/serviceinterface/ServiceInterface.h"
|
#include "fsfw/serviceinterface/ServiceInterface.h"
|
||||||
|
|
||||||
@ -11,9 +12,6 @@
|
|||||||
#include <fstream>
|
#include <fstream>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
uint16_t Clock::leapSeconds = 0;
|
|
||||||
MutexIF* Clock::timeMutex = NULL;
|
|
||||||
|
|
||||||
using SystemClock = std::chrono::system_clock;
|
using SystemClock = std::chrono::system_clock;
|
||||||
|
|
||||||
uint32_t Clock::getTicksPerSecond(void) {
|
uint32_t Clock::getTicksPerSecond(void) {
|
||||||
@ -127,6 +125,13 @@ ReturnValue_t Clock::getDateAndTime(TimeOfDay_t* time) {
|
|||||||
auto seconds = std::chrono::time_point_cast<std::chrono::seconds>(now);
|
auto seconds = std::chrono::time_point_cast<std::chrono::seconds>(now);
|
||||||
auto fraction = now - seconds;
|
auto fraction = now - seconds;
|
||||||
time_t tt = SystemClock::to_time_t(now);
|
time_t tt = SystemClock::to_time_t(now);
|
||||||
|
ReturnValue_t result = checkOrCreateClockMutex();
|
||||||
|
if (result != HasReturnvaluesIF::RETURN_OK) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
MutexGuard helper(timeMutex);
|
||||||
|
// gmtime writes its output in a global buffer which is not Thread Safe
|
||||||
|
// Therefore we have to use a Mutex here
|
||||||
struct tm* timeInfo;
|
struct tm* timeInfo;
|
||||||
timeInfo = gmtime(&tt);
|
timeInfo = gmtime(&tt);
|
||||||
time->year = timeInfo->tm_year + 1900;
|
time->year = timeInfo->tm_year + 1900;
|
||||||
|
@ -8,11 +8,9 @@
|
|||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
|
||||||
|
#include "fsfw/ipc/MutexGuard.h"
|
||||||
#include "fsfw/serviceinterface/ServiceInterface.h"
|
#include "fsfw/serviceinterface/ServiceInterface.h"
|
||||||
|
|
||||||
uint16_t Clock::leapSeconds = 0;
|
|
||||||
MutexIF* Clock::timeMutex = NULL;
|
|
||||||
|
|
||||||
uint32_t Clock::getTicksPerSecond(void) {
|
uint32_t Clock::getTicksPerSecond(void) {
|
||||||
uint32_t ticks = sysconf(_SC_CLK_TCK);
|
uint32_t ticks = sysconf(_SC_CLK_TCK);
|
||||||
return ticks;
|
return ticks;
|
||||||
@ -117,7 +115,13 @@ ReturnValue_t Clock::getDateAndTime(TimeOfDay_t* time) {
|
|||||||
// TODO errno
|
// TODO errno
|
||||||
return HasReturnvaluesIF::RETURN_FAILED;
|
return HasReturnvaluesIF::RETURN_FAILED;
|
||||||
}
|
}
|
||||||
|
ReturnValue_t result = checkOrCreateClockMutex();
|
||||||
|
if (result != HasReturnvaluesIF::RETURN_OK) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
MutexGuard helper(timeMutex);
|
||||||
|
// gmtime writes its output in a global buffer which is not Thread Safe
|
||||||
|
// Therefore we have to use a Mutex here
|
||||||
struct tm* timeInfo;
|
struct tm* timeInfo;
|
||||||
timeInfo = gmtime(&timeUnix.tv_sec);
|
timeInfo = gmtime(&timeUnix.tv_sec);
|
||||||
time->year = timeInfo->tm_year + 1900;
|
time->year = timeInfo->tm_year + 1900;
|
||||||
|
@ -6,9 +6,6 @@
|
|||||||
#include "fsfw/ipc/MutexGuard.h"
|
#include "fsfw/ipc/MutexGuard.h"
|
||||||
#include "fsfw/osal/rtems/RtemsBasic.h"
|
#include "fsfw/osal/rtems/RtemsBasic.h"
|
||||||
|
|
||||||
uint16_t Clock::leapSeconds = 0;
|
|
||||||
MutexIF* Clock::timeMutex = nullptr;
|
|
||||||
|
|
||||||
uint32_t Clock::getTicksPerSecond(void) {
|
uint32_t Clock::getTicksPerSecond(void) {
|
||||||
rtems_interval ticks_per_second = rtems_clock_get_ticks_per_second();
|
rtems_interval ticks_per_second = rtems_clock_get_ticks_per_second();
|
||||||
return static_cast<uint32_t>(ticks_per_second);
|
return static_cast<uint32_t>(ticks_per_second);
|
||||||
|
@ -91,7 +91,7 @@ ReturnValue_t CCSDSTime::convertFromCDS(Clock::TimeOfDay_t* to, const uint8_t* f
|
|||||||
if (result != HasReturnvaluesIF::RETURN_OK) {
|
if (result != HasReturnvaluesIF::RETURN_OK) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
return convertTimevalToTimeOfDay(to, &time);
|
return Clock::convertTimevalToTimeOfDay(&time, to);
|
||||||
}
|
}
|
||||||
|
|
||||||
ReturnValue_t CCSDSTime::convertFromCCS(Clock::TimeOfDay_t* to, const uint8_t* from,
|
ReturnValue_t CCSDSTime::convertFromCCS(Clock::TimeOfDay_t* to, const uint8_t* from,
|
||||||
@ -489,11 +489,6 @@ ReturnValue_t CCSDSTime::checkTimeOfDay(const Clock::TimeOfDay_t* time) {
|
|||||||
return RETURN_OK;
|
return RETURN_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
ReturnValue_t CCSDSTime::convertTimevalToTimeOfDay(Clock::TimeOfDay_t* to, timeval* from) {
|
|
||||||
// This is rather tricky. Implement only if needed. Also, if so, move to OSAL.
|
|
||||||
return UNSUPPORTED_TIME_FORMAT;
|
|
||||||
}
|
|
||||||
|
|
||||||
ReturnValue_t CCSDSTime::convertFromCDS(timeval* to, const uint8_t* from, size_t* foundLength,
|
ReturnValue_t CCSDSTime::convertFromCDS(timeval* to, const uint8_t* from, size_t* foundLength,
|
||||||
size_t maxLength) {
|
size_t maxLength) {
|
||||||
uint8_t pField = *from;
|
uint8_t pField = *from;
|
||||||
@ -583,7 +578,7 @@ ReturnValue_t CCSDSTime::convertFromCDS(Clock::TimeOfDay_t* to, const CCSDSTime:
|
|||||||
if (result != HasReturnvaluesIF::RETURN_OK) {
|
if (result != HasReturnvaluesIF::RETURN_OK) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
return CCSDSTime::convertTimevalToTimeOfDay(to, &tempTimeval);
|
return Clock::convertTimevalToTimeOfDay(&tempTimeval, to);
|
||||||
}
|
}
|
||||||
|
|
||||||
ReturnValue_t CCSDSTime::convertFromCUC(timeval* to, uint8_t pField, const uint8_t* from,
|
ReturnValue_t CCSDSTime::convertFromCUC(timeval* to, uint8_t pField, const uint8_t* from,
|
||||||
|
@ -223,7 +223,6 @@ class CCSDSTime : public HasReturnvaluesIF {
|
|||||||
uint8_t *day);
|
uint8_t *day);
|
||||||
|
|
||||||
static bool isLeapYear(uint32_t year);
|
static bool isLeapYear(uint32_t year);
|
||||||
static ReturnValue_t convertTimevalToTimeOfDay(Clock::TimeOfDay_t *to, timeval *from);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* FSFW_TIMEMANAGER_CCSDSTIME_H_ */
|
#endif /* FSFW_TIMEMANAGER_CCSDSTIME_H_ */
|
||||||
|
@ -99,6 +99,13 @@ class Clock {
|
|||||||
*/
|
*/
|
||||||
static ReturnValue_t getDateAndTime(TimeOfDay_t *time);
|
static ReturnValue_t getDateAndTime(TimeOfDay_t *time);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert to time of day struct given the POSIX timeval struct
|
||||||
|
* @param from
|
||||||
|
* @param to
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
static ReturnValue_t convertTimevalToTimeOfDay(const timeval *from, TimeOfDay_t *to);
|
||||||
/**
|
/**
|
||||||
* Converts a time of day struct to POSIX seconds.
|
* Converts a time of day struct to POSIX seconds.
|
||||||
* @param time The time of day as input
|
* @param time The time of day as input
|
||||||
@ -166,6 +173,7 @@ class Clock {
|
|||||||
|
|
||||||
static MutexIF *timeMutex;
|
static MutexIF *timeMutex;
|
||||||
static uint16_t leapSeconds;
|
static uint16_t leapSeconds;
|
||||||
|
static bool leapSecondsSet;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* FSFW_TIMEMANAGER_CLOCK_H_ */
|
#endif /* FSFW_TIMEMANAGER_CLOCK_H_ */
|
||||||
|
@ -1,7 +1,13 @@
|
|||||||
|
#include <ctime>
|
||||||
|
|
||||||
#include "fsfw/ipc/MutexGuard.h"
|
#include "fsfw/ipc/MutexGuard.h"
|
||||||
#include "fsfw/timemanager/Clock.h"
|
#include "fsfw/timemanager/Clock.h"
|
||||||
|
|
||||||
ReturnValue_t Clock::convertUTCToTT(timeval utc, timeval *tt) {
|
uint16_t Clock::leapSeconds = 0;
|
||||||
|
MutexIF* Clock::timeMutex = nullptr;
|
||||||
|
bool Clock::leapSecondsSet = false;
|
||||||
|
|
||||||
|
ReturnValue_t Clock::convertUTCToTT(timeval utc, timeval* tt) {
|
||||||
uint16_t leapSeconds;
|
uint16_t leapSeconds;
|
||||||
ReturnValue_t result = getLeapSeconds(&leapSeconds);
|
ReturnValue_t result = getLeapSeconds(&leapSeconds);
|
||||||
if (result != HasReturnvaluesIF::RETURN_OK) {
|
if (result != HasReturnvaluesIF::RETURN_OK) {
|
||||||
@ -27,12 +33,16 @@ ReturnValue_t Clock::setLeapSeconds(const uint16_t leapSeconds_) {
|
|||||||
MutexGuard helper(timeMutex);
|
MutexGuard helper(timeMutex);
|
||||||
|
|
||||||
leapSeconds = leapSeconds_;
|
leapSeconds = leapSeconds_;
|
||||||
|
leapSecondsSet = true;
|
||||||
|
|
||||||
return HasReturnvaluesIF::RETURN_OK;
|
return HasReturnvaluesIF::RETURN_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
ReturnValue_t Clock::getLeapSeconds(uint16_t *leapSeconds_) {
|
ReturnValue_t Clock::getLeapSeconds(uint16_t* leapSeconds_) {
|
||||||
if (timeMutex == nullptr) {
|
if (not leapSecondsSet) {
|
||||||
|
return HasReturnvaluesIF::RETURN_FAILED;
|
||||||
|
}
|
||||||
|
if (checkOrCreateClockMutex() != HasReturnvaluesIF::RETURN_OK) {
|
||||||
return HasReturnvaluesIF::RETURN_FAILED;
|
return HasReturnvaluesIF::RETURN_FAILED;
|
||||||
}
|
}
|
||||||
MutexGuard helper(timeMutex);
|
MutexGuard helper(timeMutex);
|
||||||
@ -42,9 +52,32 @@ ReturnValue_t Clock::getLeapSeconds(uint16_t *leapSeconds_) {
|
|||||||
return HasReturnvaluesIF::RETURN_OK;
|
return HasReturnvaluesIF::RETURN_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ReturnValue_t Clock::convertTimevalToTimeOfDay(const timeval* from, TimeOfDay_t* to) {
|
||||||
|
struct tm* timeInfo;
|
||||||
|
// According to https://en.cppreference.com/w/c/chrono/gmtime, the implementation of gmtime_s
|
||||||
|
// in the Windows CRT is incompatible with the C standard but this should not be an issue for
|
||||||
|
// this implementation
|
||||||
|
ReturnValue_t result = checkOrCreateClockMutex();
|
||||||
|
if (result != HasReturnvaluesIF::RETURN_OK) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
MutexGuard helper(timeMutex);
|
||||||
|
// gmtime writes its output in a global buffer which is not Thread Safe
|
||||||
|
// Therefore we have to use a Mutex here
|
||||||
|
timeInfo = gmtime(&from->tv_sec);
|
||||||
|
to->year = timeInfo->tm_year + 1900;
|
||||||
|
to->month = timeInfo->tm_mon + 1;
|
||||||
|
to->day = timeInfo->tm_mday;
|
||||||
|
to->hour = timeInfo->tm_hour;
|
||||||
|
to->minute = timeInfo->tm_min;
|
||||||
|
to->second = timeInfo->tm_sec;
|
||||||
|
to->usecond = from->tv_usec;
|
||||||
|
return HasReturnvaluesIF::RETURN_OK;
|
||||||
|
}
|
||||||
|
|
||||||
ReturnValue_t Clock::checkOrCreateClockMutex() {
|
ReturnValue_t Clock::checkOrCreateClockMutex() {
|
||||||
if (timeMutex == nullptr) {
|
if (timeMutex == nullptr) {
|
||||||
MutexFactory *mutexFactory = MutexFactory::instance();
|
MutexFactory* mutexFactory = MutexFactory::instance();
|
||||||
if (mutexFactory == nullptr) {
|
if (mutexFactory == nullptr) {
|
||||||
return HasReturnvaluesIF::RETURN_FAILED;
|
return HasReturnvaluesIF::RETURN_FAILED;
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
#include "version.h"
|
#include "version.h"
|
||||||
#include "fsfw/FSFWVersion.h"
|
|
||||||
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
|
||||||
|
#include "fsfw/FSFWVersion.h"
|
||||||
|
|
||||||
#ifdef major
|
#ifdef major
|
||||||
#undef major
|
#undef major
|
||||||
#endif
|
#endif
|
||||||
|
@ -29,7 +29,7 @@ class Version {
|
|||||||
}
|
}
|
||||||
|
|
||||||
friend bool operator>(const Version& v1, const Version& v2) {
|
friend bool operator>(const Version& v1, const Version& v2) {
|
||||||
return not (v1 < v2) and not (v1 == v2);
|
return not(v1 < v2) and not(v1 == v2);
|
||||||
}
|
}
|
||||||
|
|
||||||
friend bool operator<=(const Version& v1, const Version& v2) { return ((v1 == v2) or (v1 < v2)); }
|
friend bool operator<=(const Version& v1, const Version& v2) { return ((v1 == v2) or (v1 < v2)); }
|
||||||
|
@ -3,4 +3,5 @@ target_sources(${FSFW_TEST_TGT} PRIVATE
|
|||||||
testOpDivider.cpp
|
testOpDivider.cpp
|
||||||
testBitutil.cpp
|
testBitutil.cpp
|
||||||
testCRC.cpp
|
testCRC.cpp
|
||||||
|
testTimevalOperations.cpp
|
||||||
)
|
)
|
||||||
|
@ -0,0 +1,124 @@
|
|||||||
|
#include <fsfw/globalfunctions/timevalOperations.h>
|
||||||
|
|
||||||
|
#include <catch2/catch_approx.hpp>
|
||||||
|
#include <catch2/catch_test_macros.hpp>
|
||||||
|
|
||||||
|
#include "fsfw_tests/unit/CatchDefinitions.h"
|
||||||
|
|
||||||
|
TEST_CASE("TimevalTest", "[timevalOperations]") {
|
||||||
|
SECTION("Comparison") {
|
||||||
|
timeval t1;
|
||||||
|
t1.tv_sec = 1648227422;
|
||||||
|
t1.tv_usec = 123456;
|
||||||
|
timeval t2;
|
||||||
|
t2.tv_sec = 1648227422;
|
||||||
|
t2.tv_usec = 123456;
|
||||||
|
REQUIRE(t1 == t2);
|
||||||
|
REQUIRE(t2 == t1);
|
||||||
|
REQUIRE_FALSE(t1 != t2);
|
||||||
|
REQUIRE_FALSE(t2 != t1);
|
||||||
|
REQUIRE(t1 <= t2);
|
||||||
|
REQUIRE(t2 <= t1);
|
||||||
|
REQUIRE(t1 >= t2);
|
||||||
|
REQUIRE(t2 >= t1);
|
||||||
|
REQUIRE_FALSE(t1 < t2);
|
||||||
|
REQUIRE_FALSE(t2 < t1);
|
||||||
|
REQUIRE_FALSE(t1 > t2);
|
||||||
|
REQUIRE_FALSE(t2 > t1);
|
||||||
|
|
||||||
|
timeval t3;
|
||||||
|
t3.tv_sec = 1648227422;
|
||||||
|
t3.tv_usec = 123457;
|
||||||
|
REQUIRE_FALSE(t1 == t3);
|
||||||
|
REQUIRE(t1 != t3);
|
||||||
|
REQUIRE(t1 <= t3);
|
||||||
|
REQUIRE_FALSE(t3 <= t1);
|
||||||
|
REQUIRE_FALSE(t1 >= t3);
|
||||||
|
REQUIRE(t3 >= t1);
|
||||||
|
REQUIRE(t1 < t3);
|
||||||
|
REQUIRE_FALSE(t3 < t1);
|
||||||
|
REQUIRE_FALSE(t1 > t3);
|
||||||
|
REQUIRE(t3 > t1);
|
||||||
|
|
||||||
|
timeval t4;
|
||||||
|
t4.tv_sec = 1648227423;
|
||||||
|
t4.tv_usec = 123456;
|
||||||
|
REQUIRE_FALSE(t1 == t4);
|
||||||
|
REQUIRE(t1 != t4);
|
||||||
|
REQUIRE(t1 <= t4);
|
||||||
|
REQUIRE_FALSE(t4 <= t1);
|
||||||
|
REQUIRE_FALSE(t1 >= t4);
|
||||||
|
REQUIRE(t4 >= t1);
|
||||||
|
REQUIRE(t1 < t4);
|
||||||
|
REQUIRE_FALSE(t4 < t1);
|
||||||
|
REQUIRE_FALSE(t1 > t4);
|
||||||
|
REQUIRE(t4 > t1);
|
||||||
|
}
|
||||||
|
SECTION("Operators") {
|
||||||
|
timeval t1;
|
||||||
|
t1.tv_sec = 1648227422;
|
||||||
|
t1.tv_usec = 123456;
|
||||||
|
timeval t2;
|
||||||
|
t2.tv_sec = 1648227422;
|
||||||
|
t2.tv_usec = 123456;
|
||||||
|
timeval t3 = t1 - t2;
|
||||||
|
REQUIRE(t3.tv_sec == 0);
|
||||||
|
REQUIRE(t3.tv_usec == 0);
|
||||||
|
timeval t4 = t1 - t3;
|
||||||
|
REQUIRE(t4.tv_sec == 1648227422);
|
||||||
|
REQUIRE(t4.tv_usec == 123456);
|
||||||
|
timeval t5 = t3 - t1;
|
||||||
|
REQUIRE(t5.tv_sec == -1648227422);
|
||||||
|
REQUIRE(t5.tv_usec == -123456);
|
||||||
|
|
||||||
|
timeval t6;
|
||||||
|
t6.tv_sec = 1648227400;
|
||||||
|
t6.tv_usec = 999999;
|
||||||
|
|
||||||
|
timeval t7 = t6 + t1;
|
||||||
|
REQUIRE(t7.tv_sec == (1648227422ull + 1648227400ull + 1ull));
|
||||||
|
REQUIRE(t7.tv_usec == 123455);
|
||||||
|
|
||||||
|
timeval t8 = t1 - t6;
|
||||||
|
REQUIRE(t8.tv_sec == 1648227422 - 1648227400 - 1);
|
||||||
|
REQUIRE(t8.tv_usec == 123457);
|
||||||
|
|
||||||
|
double scalar = 2;
|
||||||
|
timeval t9 = t1 * scalar;
|
||||||
|
REQUIRE(t9.tv_sec == 3296454844);
|
||||||
|
REQUIRE(t9.tv_usec == 246912);
|
||||||
|
timeval t10 = scalar * t1;
|
||||||
|
REQUIRE(t10.tv_sec == 3296454844);
|
||||||
|
REQUIRE(t10.tv_usec == 246912);
|
||||||
|
timeval t11 = t6 * scalar;
|
||||||
|
REQUIRE(t11.tv_sec == (3296454800 + 1));
|
||||||
|
REQUIRE(t11.tv_usec == 999998);
|
||||||
|
|
||||||
|
timeval t12 = t1 / scalar;
|
||||||
|
REQUIRE(t12.tv_sec == 824113711);
|
||||||
|
REQUIRE(t12.tv_usec == 61728);
|
||||||
|
|
||||||
|
timeval t13 = t6 / scalar;
|
||||||
|
REQUIRE(t13.tv_sec == 824113700);
|
||||||
|
// Rounding issue
|
||||||
|
REQUIRE(t13.tv_usec == 499999);
|
||||||
|
|
||||||
|
double scalar2 = t9 / t1;
|
||||||
|
REQUIRE(scalar2 == Catch::Approx(2.0));
|
||||||
|
double scalar3 = t1 / t6;
|
||||||
|
REQUIRE(scalar3 == Catch::Approx(1.000000013));
|
||||||
|
double scalar4 = t3 / t1;
|
||||||
|
REQUIRE(scalar4 == Catch::Approx(0));
|
||||||
|
double scalar5 = t12 / t1;
|
||||||
|
REQUIRE(scalar5 == Catch::Approx(0.5));
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("timevalOperations::toTimeval") {
|
||||||
|
double seconds = 1648227422.123456;
|
||||||
|
timeval t1 = timevalOperations::toTimeval(seconds);
|
||||||
|
REQUIRE(t1.tv_sec == 1648227422);
|
||||||
|
// Allow 1 usec rounding tolerance
|
||||||
|
REQUIRE(t1.tv_usec >= 123455);
|
||||||
|
REQUIRE(t1.tv_usec <= 123457);
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
target_sources(${FSFW_TEST_TGT} PRIVATE
|
target_sources(${FSFW_TEST_TGT} PRIVATE
|
||||||
TestMessageQueue.cpp
|
TestMessageQueue.cpp
|
||||||
TestSemaphore.cpp
|
TestSemaphore.cpp
|
||||||
|
TestClock.cpp
|
||||||
)
|
)
|
||||||
|
86
tests/src/fsfw_tests/unit/osal/TestClock.cpp
Normal file
86
tests/src/fsfw_tests/unit/osal/TestClock.cpp
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
#include <fsfw/globalfunctions/timevalOperations.h>
|
||||||
|
#include <fsfw/timemanager/Clock.h>
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <catch2/catch_approx.hpp>
|
||||||
|
#include <catch2/catch_test_macros.hpp>
|
||||||
|
|
||||||
|
#include "fsfw_tests/unit/CatchDefinitions.h"
|
||||||
|
|
||||||
|
TEST_CASE("OSAL::Clock Test", "[OSAL::Clock Test]") {
|
||||||
|
SECTION("Test getClock") {
|
||||||
|
timeval time;
|
||||||
|
ReturnValue_t result = Clock::getClock_timeval(&time);
|
||||||
|
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
|
||||||
|
Clock::TimeOfDay_t timeOfDay;
|
||||||
|
result = Clock::getDateAndTime(&timeOfDay);
|
||||||
|
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
|
||||||
|
timeval timeOfDayAsTimeval;
|
||||||
|
result = Clock::convertTimeOfDayToTimeval(&timeOfDay, &timeOfDayAsTimeval);
|
||||||
|
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
|
||||||
|
// We require timeOfDayAsTimeval to be larger than time as it
|
||||||
|
// was request a few ns later
|
||||||
|
double difference = timevalOperations::toDouble(timeOfDayAsTimeval - time);
|
||||||
|
CHECK(difference >= 0.0);
|
||||||
|
CHECK(difference <= 0.005);
|
||||||
|
|
||||||
|
// Conversion in the other direction
|
||||||
|
Clock::TimeOfDay_t timevalAsTimeOfDay;
|
||||||
|
result = Clock::convertTimevalToTimeOfDay(&time, &timevalAsTimeOfDay);
|
||||||
|
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
|
||||||
|
CHECK(timevalAsTimeOfDay.year <= timeOfDay.year);
|
||||||
|
// TODO We should write TimeOfDay operators!
|
||||||
|
}
|
||||||
|
SECTION("Leap seconds") {
|
||||||
|
uint16_t leapSeconds = 0;
|
||||||
|
ReturnValue_t result = Clock::getLeapSeconds(&leapSeconds);
|
||||||
|
REQUIRE(result == HasReturnvaluesIF::RETURN_FAILED);
|
||||||
|
REQUIRE(leapSeconds == 0);
|
||||||
|
result = Clock::setLeapSeconds(18);
|
||||||
|
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
|
||||||
|
result = Clock::getLeapSeconds(&leapSeconds);
|
||||||
|
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
|
||||||
|
REQUIRE(leapSeconds == 18);
|
||||||
|
}
|
||||||
|
SECTION("usec Test") {
|
||||||
|
timeval timeAsTimeval;
|
||||||
|
ReturnValue_t result = Clock::getClock_timeval(&timeAsTimeval);
|
||||||
|
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
|
||||||
|
uint64_t timeAsUsec = 0;
|
||||||
|
result = Clock::getClock_usecs(&timeAsUsec);
|
||||||
|
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
|
||||||
|
double timeAsUsecDouble = static_cast<double>(timeAsUsec) / 1000000.0;
|
||||||
|
timeval timeAsUsecTimeval = timevalOperations::toTimeval(timeAsUsecDouble);
|
||||||
|
double difference = timevalOperations::toDouble(timeAsUsecTimeval - timeAsTimeval);
|
||||||
|
// We accept 5 ms difference
|
||||||
|
CHECK(difference >= 0.0);
|
||||||
|
CHECK(difference <= 0.005);
|
||||||
|
uint64_t timevalAsUint64 = static_cast<uint64_t>(timeAsTimeval.tv_sec) * 1000000ull +
|
||||||
|
static_cast<uint64_t>(timeAsTimeval.tv_usec);
|
||||||
|
CHECK((timeAsUsec - timevalAsUint64) >= 0);
|
||||||
|
CHECK((timeAsUsec - timevalAsUint64) <= (5 * 1000));
|
||||||
|
}
|
||||||
|
SECTION("Test j2000") {
|
||||||
|
double j2000;
|
||||||
|
timeval time;
|
||||||
|
time.tv_sec = 1648208539;
|
||||||
|
time.tv_usec = 0;
|
||||||
|
ReturnValue_t result = Clock::convertTimevalToJD2000(time, &j2000);
|
||||||
|
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
|
||||||
|
double correctJ2000 = 2459663.98772 - 2451545.0;
|
||||||
|
CHECK(j2000 == Catch::Approx(correctJ2000).margin(1.2 * 1e-8));
|
||||||
|
}
|
||||||
|
SECTION("Convert to TT") {
|
||||||
|
timeval utcTime;
|
||||||
|
utcTime.tv_sec = 1648208539;
|
||||||
|
utcTime.tv_usec = 999000;
|
||||||
|
timeval tt;
|
||||||
|
ReturnValue_t result = Clock::setLeapSeconds(27);
|
||||||
|
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
|
||||||
|
result = Clock::convertUTCToTT(utcTime, &tt);
|
||||||
|
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
|
||||||
|
CHECK(tt.tv_usec == 183000);
|
||||||
|
// The plus 1 is a own forced overflow of usecs
|
||||||
|
CHECK(tt.tv_sec == (1648208539 + 27 + 10 + 32 + 1));
|
||||||
|
}
|
||||||
|
}
|
@ -81,7 +81,8 @@ TEST_CASE("CCSDSTime Tests", "[TestCCSDSTime]") {
|
|||||||
std::string timeAscii = "2022-12-31T23:59:59.123Z";
|
std::string timeAscii = "2022-12-31T23:59:59.123Z";
|
||||||
Clock::TimeOfDay_t timeTo;
|
Clock::TimeOfDay_t timeTo;
|
||||||
const uint8_t* timeChar = reinterpret_cast<const uint8_t*>(timeAscii.c_str());
|
const uint8_t* timeChar = reinterpret_cast<const uint8_t*>(timeAscii.c_str());
|
||||||
CCSDSTime::convertFromASCII(&timeTo, timeChar, timeAscii.length());
|
auto result = CCSDSTime::convertFromASCII(&timeTo, timeChar, timeAscii.length());
|
||||||
|
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
|
||||||
REQUIRE(timeTo.year == 2022);
|
REQUIRE(timeTo.year == 2022);
|
||||||
REQUIRE(timeTo.month == 12);
|
REQUIRE(timeTo.month == 12);
|
||||||
REQUIRE(timeTo.day == 31);
|
REQUIRE(timeTo.day == 31);
|
||||||
@ -89,6 +90,19 @@ TEST_CASE("CCSDSTime Tests", "[TestCCSDSTime]") {
|
|||||||
REQUIRE(timeTo.minute == 59);
|
REQUIRE(timeTo.minute == 59);
|
||||||
REQUIRE(timeTo.second == 59);
|
REQUIRE(timeTo.second == 59);
|
||||||
REQUIRE(timeTo.usecond == Catch::Approx(123000));
|
REQUIRE(timeTo.usecond == Catch::Approx(123000));
|
||||||
|
|
||||||
|
std::string timeAscii2 = "2022-365T23:59:59.123Z";
|
||||||
|
const uint8_t* timeChar2 = reinterpret_cast<const uint8_t*>(timeAscii2.c_str());
|
||||||
|
Clock::TimeOfDay_t timeTo2;
|
||||||
|
result = CCSDSTime::convertFromCcsds(&timeTo2, timeChar2, timeAscii2.length());
|
||||||
|
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
|
||||||
|
REQUIRE(timeTo2.year == 2022);
|
||||||
|
REQUIRE(timeTo2.month == 12);
|
||||||
|
REQUIRE(timeTo2.day == 31);
|
||||||
|
REQUIRE(timeTo2.hour == 23);
|
||||||
|
REQUIRE(timeTo2.minute == 59);
|
||||||
|
REQUIRE(timeTo2.second == 59);
|
||||||
|
REQUIRE(timeTo2.usecond == Catch::Approx(123000));
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("CDS Conversions") {
|
SECTION("CDS Conversions") {
|
||||||
@ -119,6 +133,7 @@ TEST_CASE("CCSDSTime Tests", "[TestCCSDSTime]") {
|
|||||||
CHECK(cdsTime.msDay_h == 0xE0);
|
CHECK(cdsTime.msDay_h == 0xE0);
|
||||||
CHECK(cdsTime.msDay_l == 0xC5);
|
CHECK(cdsTime.msDay_l == 0xC5);
|
||||||
CHECK(cdsTime.msDay_ll == 0xC3);
|
CHECK(cdsTime.msDay_ll == 0xC3);
|
||||||
|
CHECK(cdsTime.pField == CCSDSTime::P_FIELD_CDS_SHORT);
|
||||||
|
|
||||||
// Conversion back to timeval
|
// Conversion back to timeval
|
||||||
timeval timeReturnAsTimeval;
|
timeval timeReturnAsTimeval;
|
||||||
@ -128,5 +143,56 @@ TEST_CASE("CCSDSTime Tests", "[TestCCSDSTime]") {
|
|||||||
timeval difference = timeAsTimeval - timeReturnAsTimeval;
|
timeval difference = timeAsTimeval - timeReturnAsTimeval;
|
||||||
CHECK(difference.tv_usec == 456);
|
CHECK(difference.tv_usec == 456);
|
||||||
CHECK(difference.tv_sec == 0);
|
CHECK(difference.tv_sec == 0);
|
||||||
|
|
||||||
|
Clock::TimeOfDay_t timeReturnAsTimeOfDay;
|
||||||
|
result = CCSDSTime::convertFromCDS(&timeReturnAsTimeOfDay, &cdsTime);
|
||||||
|
CHECK(result == HasReturnvaluesIF::RETURN_OK);
|
||||||
|
CHECK(timeReturnAsTimeOfDay.year == 2020);
|
||||||
|
CHECK(timeReturnAsTimeOfDay.month == 2);
|
||||||
|
CHECK(timeReturnAsTimeOfDay.day == 29);
|
||||||
|
CHECK(timeReturnAsTimeOfDay.hour == 13);
|
||||||
|
CHECK(timeReturnAsTimeOfDay.minute == 24);
|
||||||
|
CHECK(timeReturnAsTimeOfDay.second == 45);
|
||||||
|
// micro seconds precision is lost
|
||||||
|
CHECK(timeReturnAsTimeOfDay.usecond == 123000);
|
||||||
|
|
||||||
|
Clock::TimeOfDay_t timeReturnAsTodFromBuffer;
|
||||||
|
const uint8_t* buffer = reinterpret_cast<const uint8_t*>(&cdsTime);
|
||||||
|
result = CCSDSTime::convertFromCDS(&timeReturnAsTodFromBuffer, buffer, sizeof(cdsTime));
|
||||||
|
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
|
||||||
|
CHECK(timeReturnAsTodFromBuffer.year == time.year);
|
||||||
|
CHECK(timeReturnAsTodFromBuffer.month == time.month);
|
||||||
|
CHECK(timeReturnAsTodFromBuffer.day == time.day);
|
||||||
|
CHECK(timeReturnAsTodFromBuffer.hour == time.hour);
|
||||||
|
CHECK(timeReturnAsTodFromBuffer.minute == time.minute);
|
||||||
|
CHECK(timeReturnAsTodFromBuffer.second == time.second);
|
||||||
|
CHECK(timeReturnAsTodFromBuffer.usecond == 123000);
|
||||||
|
|
||||||
|
Clock::TimeOfDay_t todFromCCSDS;
|
||||||
|
result = CCSDSTime::convertFromCcsds(&todFromCCSDS, buffer, sizeof(cdsTime));
|
||||||
|
CHECK(result == HasReturnvaluesIF::RETURN_OK);
|
||||||
|
CHECK(todFromCCSDS.year == time.year);
|
||||||
|
CHECK(todFromCCSDS.month == time.month);
|
||||||
|
CHECK(todFromCCSDS.day == time.day);
|
||||||
|
CHECK(todFromCCSDS.hour == time.hour);
|
||||||
|
CHECK(todFromCCSDS.minute == time.minute);
|
||||||
|
CHECK(todFromCCSDS.second == time.second);
|
||||||
|
CHECK(todFromCCSDS.usecond == 123000);
|
||||||
|
}
|
||||||
|
SECTION("CCSDS Failures") {
|
||||||
|
Clock::TimeOfDay_t time;
|
||||||
|
time.year = 2020;
|
||||||
|
time.month = 12;
|
||||||
|
time.day = 32;
|
||||||
|
time.hour = 13;
|
||||||
|
time.minute = 24;
|
||||||
|
time.second = 45;
|
||||||
|
time.usecond = 123456;
|
||||||
|
CCSDSTime::Ccs_mseconds to;
|
||||||
|
auto result = CCSDSTime::convertToCcsds(&to, &time);
|
||||||
|
REQUIRE(result == CCSDSTime::INVALID_TIME_FORMAT);
|
||||||
|
CCSDSTime::Ccs_seconds to2;
|
||||||
|
result = CCSDSTime::convertToCcsds(&to2, &time);
|
||||||
|
REQUIRE(result == CCSDSTime::INVALID_TIME_FORMAT);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -17,15 +17,15 @@ TEST_CASE("Version API Tests", "[TestVersionAPI]") {
|
|||||||
fsfw::Version v1 = fsfw::Version(1, 1, 1);
|
fsfw::Version v1 = fsfw::Version(1, 1, 1);
|
||||||
fsfw::Version v2 = fsfw::Version(1, 1, 1);
|
fsfw::Version v2 = fsfw::Version(1, 1, 1);
|
||||||
REQUIRE(v1 == v2);
|
REQUIRE(v1 == v2);
|
||||||
REQUIRE(not (v1 < v2));
|
REQUIRE(not(v1 < v2));
|
||||||
REQUIRE(not (v1 > v2));
|
REQUIRE(not(v1 > v2));
|
||||||
REQUIRE(v1 <= v2);
|
REQUIRE(v1 <= v2);
|
||||||
REQUIRE(v1 >= v2);
|
REQUIRE(v1 >= v2);
|
||||||
v1.revision -= 1;
|
v1.revision -= 1;
|
||||||
REQUIRE(v1 != v2);
|
REQUIRE(v1 != v2);
|
||||||
REQUIRE(not (v1 == v2));
|
REQUIRE(not(v1 == v2));
|
||||||
REQUIRE(not (v1 > v2));
|
REQUIRE(not(v1 > v2));
|
||||||
REQUIRE(not (v1 >= v2));
|
REQUIRE(not(v1 >= v2));
|
||||||
REQUIRE(v1 < v2);
|
REQUIRE(v1 < v2);
|
||||||
REQUIRE(v1 <= v2);
|
REQUIRE(v1 <= v2);
|
||||||
v1.revision += 1;
|
v1.revision += 1;
|
||||||
@ -33,60 +33,60 @@ TEST_CASE("Version API Tests", "[TestVersionAPI]") {
|
|||||||
REQUIRE(v1 != v2);
|
REQUIRE(v1 != v2);
|
||||||
REQUIRE(v1 < v2);
|
REQUIRE(v1 < v2);
|
||||||
REQUIRE(v1 <= v2);
|
REQUIRE(v1 <= v2);
|
||||||
REQUIRE(not (v1 == v2));
|
REQUIRE(not(v1 == v2));
|
||||||
REQUIRE(not (v1 > v2));
|
REQUIRE(not(v1 > v2));
|
||||||
REQUIRE(not (v1 >= v2));
|
REQUIRE(not(v1 >= v2));
|
||||||
v1.minor += 1;
|
v1.minor += 1;
|
||||||
v1.major -= 1;
|
v1.major -= 1;
|
||||||
REQUIRE(v1 != v2);
|
REQUIRE(v1 != v2);
|
||||||
REQUIRE(v1 < v2);
|
REQUIRE(v1 < v2);
|
||||||
REQUIRE(v1 <= v2);
|
REQUIRE(v1 <= v2);
|
||||||
REQUIRE(not (v1 == v2));
|
REQUIRE(not(v1 == v2));
|
||||||
REQUIRE(not (v1 > v2));
|
REQUIRE(not(v1 > v2));
|
||||||
REQUIRE(not (v1 >= v2));
|
REQUIRE(not(v1 >= v2));
|
||||||
v1.major += 1;
|
v1.major += 1;
|
||||||
REQUIRE(v1 == v2);
|
REQUIRE(v1 == v2);
|
||||||
REQUIRE(v1 <= v2);
|
REQUIRE(v1 <= v2);
|
||||||
REQUIRE(v1 >= v2);
|
REQUIRE(v1 >= v2);
|
||||||
REQUIRE(not (v1 != v2));
|
REQUIRE(not(v1 != v2));
|
||||||
REQUIRE(not (v1 > v2));
|
REQUIRE(not(v1 > v2));
|
||||||
REQUIRE(not (v1 < v2));
|
REQUIRE(not(v1 < v2));
|
||||||
v1.major += 1;
|
v1.major += 1;
|
||||||
v1.minor -= 1;
|
v1.minor -= 1;
|
||||||
REQUIRE(v1 != v2);
|
REQUIRE(v1 != v2);
|
||||||
REQUIRE(v1 > v2);
|
REQUIRE(v1 > v2);
|
||||||
REQUIRE(v1 >= v2);
|
REQUIRE(v1 >= v2);
|
||||||
REQUIRE(not (v1 == v2));
|
REQUIRE(not(v1 == v2));
|
||||||
REQUIRE(not (v1 < v2));
|
REQUIRE(not(v1 < v2));
|
||||||
REQUIRE(not (v1 <= v2));
|
REQUIRE(not(v1 <= v2));
|
||||||
v1.major -= 1;
|
v1.major -= 1;
|
||||||
v1.minor += 2;
|
v1.minor += 2;
|
||||||
v1.revision -= 1;
|
v1.revision -= 1;
|
||||||
REQUIRE(v1 != v2);
|
REQUIRE(v1 != v2);
|
||||||
REQUIRE(v1 > v2);
|
REQUIRE(v1 > v2);
|
||||||
REQUIRE(v1 >= v2);
|
REQUIRE(v1 >= v2);
|
||||||
REQUIRE(not (v1 == v2));
|
REQUIRE(not(v1 == v2));
|
||||||
REQUIRE(not (v1 < v2));
|
REQUIRE(not(v1 < v2));
|
||||||
REQUIRE(not (v1 <= v2));
|
REQUIRE(not(v1 <= v2));
|
||||||
v1.minor -= 1;
|
v1.minor -= 1;
|
||||||
v1.revision += 2;
|
v1.revision += 2;
|
||||||
REQUIRE(v1 != v2);
|
REQUIRE(v1 != v2);
|
||||||
REQUIRE(v1 > v2);
|
REQUIRE(v1 > v2);
|
||||||
REQUIRE(v1 >= v2);
|
REQUIRE(v1 >= v2);
|
||||||
REQUIRE(not (v1 == v2));
|
REQUIRE(not(v1 == v2));
|
||||||
REQUIRE(not (v1 < v2));
|
REQUIRE(not(v1 < v2));
|
||||||
REQUIRE(not (v1 <= v2));
|
REQUIRE(not(v1 <= v2));
|
||||||
v1.revision -= 1;
|
v1.revision -= 1;
|
||||||
REQUIRE(v1 == v2);
|
REQUIRE(v1 == v2);
|
||||||
REQUIRE(v1 <= v2);
|
REQUIRE(v1 <= v2);
|
||||||
REQUIRE(v1 >= v2);
|
REQUIRE(v1 >= v2);
|
||||||
REQUIRE(not (v1 != v2));
|
REQUIRE(not(v1 != v2));
|
||||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||||
sif::info << "v" << fsfw::FSFW_VERSION << std::endl;
|
sif::info << "v" << fsfw::FSFW_VERSION << std::endl;
|
||||||
#endif
|
#endif
|
||||||
char verString[10] = {};
|
char verString[10] = {};
|
||||||
fsfw::FSFW_VERSION.getVersion(verString, sizeof(verString));
|
fsfw::FSFW_VERSION.getVersion(verString, sizeof(verString));
|
||||||
#if FSFW_DISABLE_PRINTOUT == 0
|
#if FSFW_DISABLE_PRINTOUT == 0
|
||||||
printf("v%s\n",verString);
|
printf("v%s\n", verString);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user