diff --git a/include/ntcore.h b/include/ntcore.h index 8c1aba1139..f17d11f880 100644 --- a/include/ntcore.h +++ b/include/ntcore.h @@ -57,7 +57,7 @@ struct NT_String { /** NetworkTables Entry Value. Note this is a typed union. */ struct NT_Value { enum NT_Type type; - unsigned long last_change; + unsigned long long last_change; union { int v_boolean; double v_double; @@ -90,7 +90,7 @@ struct NT_EntryInfo { unsigned int flags; /** Timestamp of last change to entry (type or value). */ - unsigned long last_change; + unsigned long long last_change; }; /** NetworkTables Connection Information */ @@ -98,7 +98,7 @@ struct NT_ConnectionInfo { struct NT_String remote_id; const char *remote_name; unsigned int remote_port; - long last_update; + unsigned long long last_update; unsigned int protocol_version; }; @@ -301,6 +301,9 @@ void NT_InitString(struct NT_String *str); void NT_DisposeConnectionInfoArray(struct NT_ConnectionInfo *arr, size_t count); +/* timestamp */ +unsigned long long NT_Now(void); + #ifdef __cplusplus } #endif diff --git a/src/support/timestamp.cpp b/src/support/timestamp.cpp new file mode 100644 index 0000000000..cc62577a14 --- /dev/null +++ b/src/support/timestamp.cpp @@ -0,0 +1,106 @@ +#include "timestamp.h" + +#include +#include + +#ifdef _WIN32 +#include +#else +#include +#endif + +// offset in microseconds +static unsigned long long +zerotime() +{ +#ifdef _WIN32 + FILETIME ft; + unsigned long long tmpres =0; + // 100-nanosecond intervals since January 1, 1601 (UTC) + // which means 0.1 us + GetSystemTimeAsFileTime(&ft); + tmpres |= ft.dwHighDateTime; + tmpres <<= 32; + tmpres |= ft.dwLowDateTime; + // January 1st, 1970 - January 1st, 1601 UTC ~ 369 years + // or 116444736000000000 us + static const unsigned long long deltaepoch = 116444736000000000ull; + tmpres -= deltaepoch; + return tmpres; +#else + timespec ts; + + clock_gettime(CLOCK_REALTIME, &ts); + + // in 100-ns intervals + return static_cast(ts.tv_sec) * 10000000u + + static_cast(ts.tv_nsec) / 100u; +#endif +} + +static unsigned long long +timestamp() +{ +#ifdef _WIN32 + LARGE_INTEGER li; + QueryPerformanceCounter(&li); + // there is an imprecision with the initial value, + // but what matters is that timestamps are monotonic and consistent + return static_cast(li.QuadPart); +#else + timespec ts; + + // cannot fail, parameters are correct and we checked earlier we can + // access the clock + clock_gettime(CLOCK_MONOTONIC, &ts); + // in ns + return static_cast(ts.tv_sec) * 1000000000u + + static_cast(ts.tv_nsec); +#endif +} + +static unsigned long long +update_frequency() +{ +#ifdef _WIN32 + LARGE_INTEGER li; + if (!QueryPerformanceFrequency(&li) || !li.QuadPart) + { + // log something + std::terminate(); + } + return static_cast(li.QuadPart); +#else + timespec ts; + + if (clock_getres(CLOCK_MONOTONIC, &ts) < 0) + { + // log error + std::terminate(); + } + + assert(!ts.tv_sec); + + // this is the precision of the clock, we want the number of updates per + // second, which is 1 / ts.tvnsec * 1,000,000,000 + static const unsigned long long billion = 1000000000; + + return billion / static_cast(ts.tv_nsec); +#endif +} + +static const unsigned long long zerotime_val = zerotime(); +static const unsigned long long offset_val = timestamp(); +static const unsigned long long frequency_val = update_frequency(); + +unsigned long long +NT_Now() +{ + assert(offset_val > 0u); + assert(frequency_val > 0u); + unsigned long long delta = timestamp() - offset_val; + // because the frequency is in update per seconds, we have to multiply the + // delta by 10,000,000 + unsigned long long delta_in_us = delta * 10000000u / frequency_val; + return delta_in_us + zerotime_val; +} diff --git a/src/support/timestamp.h b/src/support/timestamp.h new file mode 100644 index 0000000000..70d6a82e19 --- /dev/null +++ b/src/support/timestamp.h @@ -0,0 +1,12 @@ +#ifndef TIMESTAMP_H_ +#define TIMESTAMP_H_ + +#ifdef __cplusplus +extern "C" { +#endif +unsigned long long NT_Now(void); +#ifdef __cplusplus +} +#endif + +#endif /* TIMESTAMP_H_ */