diff --git a/inc/Helpers/TimeHelper.hpp b/inc/Helpers/TimeHelper.hpp index eae6d844e7cd1b8baa3609dc56eb012db173a245..42de69837b9e7583f342244d896f783203aab5cc 100644 --- a/inc/Helpers/TimeHelper.hpp +++ b/inc/Helpers/TimeHelper.hpp @@ -2,9 +2,26 @@ #define ECSS_SERVICES_TIMEHELPER_HPP #include <cstdint> -#include <ctime> #include <Message.hpp> +/** + * The time and date provided from Real Time Clock(Real Time Clock) + * + * Note: + * This struct is similar to the `struct tm` of <ctime> library but it is more embedded-friendly + * + * For the current implementation this struct takes dummy values, because RTC hasn't been + * implemented + */ +struct TimeAndDate { + uint16_t year; + uint8_t month; + uint8_t day; + uint8_t hour; + uint8_t minute; + uint8_t second; +}; + /** * This class formats the spacecraft time and cooperates closely with the ST[09] time management. * The ECSS standard supports two time formats: the CUC and CSD that are described in @@ -12,11 +29,34 @@ * Universal Time) * * Note: - * Since this code is UTC-based, the leap second correction must be made and leap seconds should - * be considered in the difference between timestamps if a critical time-difference is needed + * Since this code is UTC-based, the leap second correction must be made. The leap seconds that + * have been occured between timestamps should be considered if a critical time-difference is needed * */ class TimeHelper { +private: + int SecondsPerMinute = 60; + int SecondsPerHour = 3600; + int SecondsPerDay = 86400; + int DaysOfMonth[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + + /** + * @param year The year that will be examined if it is a leap year(366 days) + * @return if the /p is a leap year returns true and if it isn't returns false + */ + bool IsLeapYear(uint16_t year); + + /** + * Make GMT(UTC) time. This is a reimplemented mktime() of <ctime> library in an + * embedded systems way + * + * @param timeInfo the time information/data from the RTC(UTC format) + * @return the elapsed seconds between a given UTC date(after the Unix epoch) and Unix epoch + * (1/1/1970 00:00:00) + * @todo change the epoch for computer-efficiency + */ + uint32_t mkgmtime(struct TimeAndDate &timeInfo); + public: /** @@ -33,7 +73,20 @@ public: * @todo declare the implicit P-field * @todo check if we need milliseconds */ - static uint64_t implementCDSTimeFormat(struct tm* timeInfo); + static uint64_t implementCDSTimeFormat(struct TimeAndDate &timeInfo); + + /** + * Dummy function created only to access mkgmtime for testing + * + * @todo Delete this function + */ + uint32_t get_mkgmtime(struct TimeAndDate &timeInfo) { + return mkgmtime(timeInfo); + } + }; +// used to access `mkgmtime` function in the static `implementCDSTimeFormat` function +static TimeHelper mkgmtimeAccess; + #endif //ECSS_SERVICES_TIMEHELPER_HPP diff --git a/inc/MessageParser.hpp b/inc/MessageParser.hpp index 0af0afbf7bf55f278c9bc69f27688dc56b4560b6..922ac97d6d284a8b9d433cd85a7c66e77d6d571e 100644 --- a/inc/MessageParser.hpp +++ b/inc/MessageParser.hpp @@ -32,7 +32,7 @@ public: * @param length The size of the message * @return A new object that represents the parsed message */ - Message parse(uint8_t * data, uint32_t length); + Message parse(uint8_t *data, uint32_t length); private: /** diff --git a/inc/Services/TimeManagementService.hpp b/inc/Services/TimeManagementService.hpp index d75c377684510eb2c72d55e8badd2a4c3cfc79bf..090a04e5c1457ad23a76150a5cf6b4f25d9b38f2 100644 --- a/inc/Services/TimeManagementService.hpp +++ b/inc/Services/TimeManagementService.hpp @@ -1,6 +1,7 @@ #ifndef ECSS_SERVICES_TIMEMANAGEMENTSERVICE_HPP #define ECSS_SERVICES_TIMEMANAGEMENTSERVICE_HPP + #include <Service.hpp> #include "Helpers/TimeHelper.hpp" @@ -26,27 +27,30 @@ * @todo Declare the time accuracy that the standard claims in the spacecraft * time reference section(6.9.3.d,e) */ + class TimeManagementService : public Service { public: TimeManagementService() { serviceType = 9; } - - /** + +/** * TM[9,3] CDS time report * * This function sends reports with the spacecraft time that is formatted according to the CDS * time code format(check class `TimeHelper` for the format) * + * @param timeInfo the time information/data from the RTC(UTC format) * @todo check if we need spacecraft time reference status * @todo ECSS standard claims: <<The time reports generated by the time reporting subservice * are spacecraft time packets. A spacecraft time packet does not carry the message type, * consisting of the service type and message subtype.>> Check if we need to implement that * or should ignore the standard? */ - void cdsTimeReport(); - /** + void cdsTimeReport(struct TimeAndDate &timeInfo); + + /** * TC[9,128] CDS time request * * This function is a custom subservice(mission specific) with message type 128(as defined @@ -57,8 +61,10 @@ public: * @param messageTime The class-member `data` of /p has the data for the time configuration * (a CUC format as described in the documentation of the class TimeHelper) */ - void parseTime(Message &messageTime); + + void parseTime(uint8_t *timeData, uint8_t length); }; #endif //ECSS_SERVICES_TIMEMANAGEMENTSERVICE_HPP + diff --git a/src/Helpers/TimeHelper.cpp b/src/Helpers/TimeHelper.cpp index 0920fab46c8b6dfdbae557b876a39366291babf9..648c783c082f24b210d1c2e25eaabf5ce0092ec6 100644 --- a/src/Helpers/TimeHelper.cpp +++ b/src/Helpers/TimeHelper.cpp @@ -1,32 +1,50 @@ #include "Helpers/TimeHelper.hpp" -uint64_t TimeHelper::implementCDSTimeFormat(struct tm* timeInfo) { +bool TimeHelper::IsLeapYear(uint16_t year) { + + if (year % 4 != 0) return false; + if (year % 100 != 0) return true; + return (year % 400) == 0; +} + +uint32_t TimeHelper::mkgmtime(struct TimeAndDate &timeInfo) { + + uint32_t secs = 0; + for (uint16_t y = 1970; y < timeInfo.year; ++y) + secs += (IsLeapYear(y) ? 366 : 365) * SecondsPerDay; + for (uint16_t m = 1; m < timeInfo.month; ++m) { + secs += DaysOfMonth[m - 1] * SecondsPerDay; + if (m == 2 && IsLeapYear(timeInfo.year)) + secs += SecondsPerDay; + } + secs += (timeInfo.day - 1) * SecondsPerDay; + secs += timeInfo.hour * SecondsPerHour; + secs += timeInfo.minute * SecondsPerMinute; + secs += timeInfo.second; + return secs; +} + +uint64_t TimeHelper::implementCDSTimeFormat(struct TimeAndDate &timeInfo) { /** * Define the T-field. The total number of octets for the implementation of T-field is 6(2 for * the `DAY` and 4 for the `ms of day` */ - /** - * Elapsed seconds between a given date from `timeInfo`(UTC time) and epoch 1 January 1970 - * 00:00:00(hours:minutes:seconds) - * - * @todo WARNING: evaluate the mktime() (Is it computer efficient and embedded - * systems-compliant?) - */ - time_t seconds = mktime(timeInfo); + + uint32_t seconds = mkgmtimeAccess.mkgmtime(timeInfo); /** * The `DAY` segment, 16 bits as defined from standard. Actually the days passed from an * Agency-defined epoch,that it will be 1 January 1970(1/1/1970) 00:00:00(hours:minutes:seconds) * This epoch is configured from the current implementation, using mktime() function */ - uint16_t elapsedDays = static_cast<uint16_t>(seconds/86400); + uint16_t elapsedDays = static_cast<uint16_t>(seconds / 86400); /** * The `ms of day` segment, 32 bits as defined in standard. The `ms of the day` and DAY` * should give the time passed from the defined epoch (1/1/1970) */ - uint32_t msOfDay = static_cast<uint32_t >((seconds%86400)*1000); + uint32_t msOfDay = static_cast<uint32_t >((seconds % 86400) * 1000); /** * Define CDS time format diff --git a/src/Services/TimeManagementService.cpp b/src/Services/TimeManagementService.cpp index c23b2356d5f7b953e9410dfaf73f679e9834a575..9087cb2fa58e8b6c73cb802e19d77ff439909780 100644 --- a/src/Services/TimeManagementService.cpp +++ b/src/Services/TimeManagementService.cpp @@ -1,25 +1,18 @@ #include "Services/TimeManagementService.hpp" -void TimeManagementService::cdsTimeReport() { +void TimeManagementService::cdsTimeReport(struct TimeAndDate &timeInfo) { // TM[9,3] CDS time report Message timeReport = createTM(3); - /** - * For the time being we will use C++ functions to get a time value, but this will change - * when the RTC will be implemented - */ - time_t currTime = time(nullptr); // seconds have passed since 00:00:00 GMT, Jan 1, 1970 - struct tm* timeInfo = gmtime(&currTime); // UTC time - - uint64_t timeFormat = TimeHelper::implementCDSTimeFormat(timeInfo); // store the return value + uint64_t timeFormat = TimeHelper::implementCDSTimeFormat(timeInfo); timeReport.appendByte(static_cast<uint8_t >(timeFormat >> 32)); // append the first byte - timeReport.appendWord(static_cast<uint32_t >(timeFormat)); // append the rest bytes(4 bytes) + timeReport.appendWord(static_cast<uint32_t >(timeFormat)); // append the rest 4 bytes storeMessage(timeReport); } -void TimeManagementService::parseTime(Message &messageTime) { +void TimeManagementService::parseTime(uint8_t *timeData, uint8_t lenght) { } diff --git a/src/main.cpp b/src/main.cpp index ef935aa7a20a88840e3a597ff8a5743414a9f00b..7f49b9d59dd818882c6b570e3d8681fc90a6afd0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -174,7 +174,15 @@ int main() { // ST[09] test TimeManagementService timeReport; - timeReport.cdsTimeReport(); + struct TimeAndDate timeInfo = {0}; + // 10/04/1998 10:15:00 + timeInfo.year = 1998; + timeInfo.month = 4; + timeInfo.day = 10; + timeInfo.hour = 10; + timeInfo.minute = 15; + timeInfo.second = 0; + timeReport.cdsTimeReport(timeInfo); // ST[05] (5,5 to 5,8) test [works] EventReportService::Event eventIDs[] = {EventReportService::HighSeverityUnknownEvent, diff --git a/test/Helpers/TimeHelper.cpp b/test/Helpers/TimeHelper.cpp index 0b23a2a05d7bb06a032bbfee1c51f2baa6d2f23d..f14a20474656f527af2da842ac54f0a480223de0 100644 --- a/test/Helpers/TimeHelper.cpp +++ b/test/Helpers/TimeHelper.cpp @@ -2,9 +2,36 @@ #include "Helpers/TimeHelper.hpp" TEST_CASE("Time format implementation", "[CUC]") { - // very simple tests for the TimeHelper + struct TimeAndDate timeInfo = {0}; + // 10/04/1998 10:15:00 + timeInfo.year = 1998; + timeInfo.month = 4; + timeInfo.day = 10; + timeInfo.hour = 10; + timeInfo.minute = 15; + timeInfo.second = 0; + + TimeHelper time; + uint32_t currTime = time.get_mkgmtime(timeInfo); + + uint16_t elapsedDays = currTime/86400; + uint32_t msOfDay = currTime % 86400 * 1000; + uint64_t timeFormat = (static_cast<uint64_t>(elapsedDays) << 32 | msOfDay); + CHECK(TimeHelper::implementCDSTimeFormat(timeInfo) == timeFormat); + + // 1/1/2019 00:00:00 + timeInfo.year = 2019; + timeInfo.month = 1; + timeInfo.day = 1; + timeInfo.hour = 0; + timeInfo.minute = 0; + timeInfo.second = 0; + + currTime = time.get_mkgmtime(timeInfo); + + elapsedDays = currTime/86400; + msOfDay = currTime % 86400 * 1000; + timeFormat = (static_cast<uint64_t>(elapsedDays) << 32 | msOfDay); + CHECK(TimeHelper::implementCDSTimeFormat(timeInfo) == timeFormat); - CHECK(TimeHelper::implementCUCTimeFormat(60) == 0b11110000110010); - CHECK(TimeHelper::implementCUCTimeFormat(1000) == 0x3E832); - CHECK(TimeHelper::implementCUCTimeFormat(1200) == 0x4B032); } diff --git a/test/Services/TimeManagementService.cpp b/test/Services/TimeManagementService.cpp index 714c99e144480c5037a286a5dcdada334f06d145..ad3bf17d51a51f247130028e11cd432ab8101d6c 100644 --- a/test/Services/TimeManagementService.cpp +++ b/test/Services/TimeManagementService.cpp @@ -2,15 +2,54 @@ #include <Services/TimeManagementService.hpp> #include "ServiceTests.hpp" -TEST_CASE("TM[9,2]", "[service][st09]") { - TimeManagementService timeFormat; +TEST_CASE("TM[9,3]", "[service][st09]") { + TimeManagementService timeService; - uint32_t seconds; - seconds = time(nullptr); + struct TimeAndDate timeInfo = {0}; + // 10/04/1998 10:15:00 + timeInfo.year = 1998; + timeInfo.month = 4; + timeInfo.day = 10; + timeInfo.hour = 10; + timeInfo.minute = 15; + timeInfo.second = 0; - timeFormat.cucTimeReport(); + TimeHelper time; + uint32_t currTime = time.get_mkgmtime(timeInfo); + + uint16_t elapsedDays = currTime/86400; + uint32_t msOfDay = currTime % 86400 * 1000; + uint64_t timeFormat = (static_cast<uint64_t>(elapsedDays) << 32 | msOfDay); + + timeService.cdsTimeReport(timeInfo); Message response = ServiceTests::get(0); - CHECK(response.readByte() == 50); - CHECK(response.readWord() == seconds); + CHECK(response.serviceType == 9); + CHECK(response.messageType == 3); + CHECK(response.packetType == Message::TM); + CHECK(response.readByte() == static_cast<uint8_t>((timeFormat >> 32))); + CHECK(response.readWord() == static_cast<uint32_t>(timeFormat)); + + + // 1/1/2019 00:00:00 + timeInfo.year = 2019; + timeInfo.month = 1; + timeInfo.day = 1; + timeInfo.hour = 0; + timeInfo.minute = 0; + timeInfo.second = 0; + + currTime = time.get_mkgmtime(timeInfo); + + elapsedDays = currTime/86400; + msOfDay = currTime % 86400 * 1000; + timeFormat = (static_cast<uint64_t>(elapsedDays) << 32 | msOfDay); + + timeService.cdsTimeReport(timeInfo); + response = ServiceTests::get(1); + CHECK(response.serviceType == 9); + CHECK(response.messageType == 3); + CHECK(response.packetType == Message::TM); + CHECK(response.readByte() == static_cast<uint8_t>((timeFormat >> 32))); + CHECK(response.readWord() == static_cast<uint32_t>(timeFormat)); }