From a0623668a72dd7a96b404988bea2b87a6b81c4b0 Mon Sep 17 00:00:00 2001 From: Thomas Kolb Date: Sat, 2 Nov 2024 16:17:52 +0100 Subject: [PATCH] Do all time calculations in uint64_t This prevents loss of precision that occurs with double-precision floats if timestamps become very large. Timestamps are already large if they contain a UNIX time value (requires 60 bits; double has 53 bit resolution). --- impl/src/main.c | 24 ++++++++-------- impl/src/sdr/sdr.c | 10 +++---- impl/src/utils.c | 28 +++++++++---------- impl/src/utils.h | 25 +++++++++++++++-- impl/test/layer2_over_udp/l2udptest_client.c | 26 ++++++++--------- .../layer2_over_udp/l2udptest_digipeater.c | 26 ++++++++--------- 6 files changed, 80 insertions(+), 59 deletions(-) diff --git a/impl/src/main.c b/impl/src/main.c index b011776..392f281 100644 --- a/impl/src/main.c +++ b/impl/src/main.c @@ -50,7 +50,7 @@ static int m_tunfd = -1; static bool m_running = true; -static double next_tx_switch_time = 0.0; +static uint64_t next_tx_switch_time = 0; static rx_stats_t m_rx_stats; @@ -71,7 +71,7 @@ static void signal_handler(int signal, siginfo_t *info, void *ctx) static void block_tx_for(unsigned offset_ms) { - next_tx_switch_time = get_hires_time() + (double)offset_ms * 0.001; + next_tx_switch_time = get_hires_time() + HRTIME_MS(offset_ms); } void print_complex_array(const char *varname, float complex const *array, size_t len) @@ -186,7 +186,7 @@ int main(int argc, char **argv) bool on_air = true; - srand((int)(get_hires_time() * 1e6)); + srand(get_hires_time()); // ** Initialize ** @@ -233,18 +233,18 @@ int main(int argc, char **argv) unsigned rx_retries = 0; - double old = get_hires_time(); + uint64_t old = get_hires_time(); size_t total_samples = 0; - double next_stats_print_time = old + 0.5; + uint64_t next_stats_print_time = old + HRTIME_MS(500); - double retransmit_time = 0.0; + uint64_t retransmit_time = 0; while(m_running) { - double now = get_hires_time(); + uint64_t now = get_hires_time(); - if(retransmit_time != 0.0 && now >= retransmit_time) { + if(retransmit_time != 0 && now >= retransmit_time) { LOG(LVL_INFO, "Retransmit triggered."); - retransmit_time = 0.0; + retransmit_time = 0; layer2_tx_restart(&l2tx); } @@ -325,7 +325,7 @@ int main(int argc, char **argv) RESULT_CHECK(sdr_start_rx(&sdr)); on_air = false; - retransmit_time = get_hires_time() + 1.0 + 1.0 * rand() / RAND_MAX; + retransmit_time = get_hires_time() + HRTIME_SEC(1) + HRTIME_SEC(1.0 * rand() / RAND_MAX); block_tx_for(TX_SWITCH_BACKOFF_AFTER_RX_ON); } @@ -360,9 +360,9 @@ int main(int argc, char **argv) total_samples += n_rf_samples; - double new = get_hires_time(); + uint64_t new = get_hires_time(); if(new >= next_stats_print_time) { - double rate = total_samples / (new - old); + double rate = total_samples * 1e9 / (new - old); LOG(LVL_INFO, "\nEstimated rate: %.3f MS/s", rate / 1e6); LOG(LVL_INFO, "Receiver statistics:"); LOG(LVL_INFO, " Preambles found: %8zd", m_rx_stats.preambles_found); diff --git a/impl/src/sdr/sdr.c b/impl/src/sdr/sdr.c index e5181e0..d3a7106 100644 --- a/impl/src/sdr/sdr.c +++ b/impl/src/sdr/sdr.c @@ -99,9 +99,9 @@ static int tx_callback(hackrf_transfer *transfer) return HACKRF_ERROR_OTHER; } - if(sdr_ctx->tx_start_time == 0.0) { + if(sdr_ctx->tx_start_time == 0) { sdr_ctx->tx_start_time = get_hires_time(); - sdr_ctx->tx_duration = 10e-3; // give a little headroom + sdr_ctx->tx_duration = HRTIME_MS(10); // give a little headroom LOG(LVL_INFO, "TX time tracking reset: start = %.3f.", sdr_ctx->tx_start_time); } @@ -112,7 +112,7 @@ static int tx_callback(hackrf_transfer *transfer) if(samples_read != 0) { // only add time if any actual samples were transmitted - sdr_ctx->tx_duration += (double)samples_requested / SDR_TX_SAMPLING_RATE; + sdr_ctx->tx_duration += HRTIME_SEC((double)samples_requested / SDR_TX_SAMPLING_RATE); } LOG(LVL_DEBUG, "copied %u samples to HackRF.", samples_read); @@ -377,8 +377,8 @@ result_t sdr_flush_tx_buffer(sdr_ctx_t *ctx) return 0; } - double now = get_hires_time(); - double end = ctx->tx_start_time + ctx->tx_duration; + uint64_t now = get_hires_time(); + uint64_t end = ctx->tx_start_time + ctx->tx_duration; if(sem_post(&ctx->buf_sem) < 0) { LOG(LVL_ERR, "sem_post: %s", strerror(errno)); diff --git a/impl/src/utils.c b/impl/src/utils.c index d58b8ec..71e8f97 100644 --- a/impl/src/utils.c +++ b/impl/src/utils.c @@ -76,11 +76,23 @@ err_close: return false; } -double get_hires_time(void) +uint64_t get_hires_time(void) { struct timespec clk; clock_gettime(CLOCK_MONOTONIC, &clk); - return clk.tv_sec + 1e-9 * clk.tv_nsec; + return clk.tv_sec * 1000000000ULL + (uint64_t)clk.tv_nsec; +} + +void sleep_until(uint64_t hires_time) +{ + struct timespec tv; + int ret; + + tv.tv_sec = hires_time / 1000000000ULL; + tv.tv_nsec = hires_time % 1000000000ULL; + do { + ret = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &tv, NULL); + } while(ret == EINTR); } void fsleep(double d) @@ -93,18 +105,6 @@ void fsleep(double d) nanosleep(&ts, NULL); } -void sleep_until(double hires_time) -{ - struct timespec tv; - int ret; - - tv.tv_sec = hires_time; - tv.tv_nsec = (uint64_t)(1e9 * hires_time) % 1000000000; - do { - ret = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &tv, NULL); - } while(ret == EINTR); -} - void hexdump(const uint8_t *data, size_t len) { static const char lut[16] = "0123456789ABCDEF"; diff --git a/impl/src/utils.h b/impl/src/utils.h index 30062ea..1040870 100644 --- a/impl/src/utils.h +++ b/impl/src/utils.h @@ -11,6 +11,10 @@ #include #include +#define HRTIME_US(x) ((uint64_t)(1000ULL * (x))) +#define HRTIME_MS(x) ((uint64_t)(1000000ULL * (x))) +#define HRTIME_SEC(x) ((uint64_t)(1000000000ULL * (x))) + /*! Dump a array of complex numbers. * * \param data Pointer to the data to dump. @@ -30,9 +34,26 @@ bool dump_array_cf(const float complex *data, size_t n, float T, const char *fil */ bool dump_array_f(const float *data, size_t n, float T, const char *filename); -void sleep_until(double hires_time); + +/*! Sleep until the given absolute timestamp in ns. + * + * The current timestamp can be retrieved using \ref get_hires_time(). + * + * \param hires_time The resume timestamp in ns. + */ +void sleep_until(uint64_t hires_time); + + +/*! Returns the current high-resulution timestamp. + * + * This timestamp comes from the CLOCK_MONOTONIC source and has no defined relation to the wall-clock time. It can be used to calculate intervals, though. + * + * \returns A timestamp in nanosecond resolution. + */ +uint64_t get_hires_time(void); + + void fsleep(double d); -double get_hires_time(void); void hexdump(const uint8_t *data, size_t len); diff --git a/impl/test/layer2_over_udp/l2udptest_client.c b/impl/test/layer2_over_udp/l2udptest_client.c index 82d7efd..1d53eab 100644 --- a/impl/test/layer2_over_udp/l2udptest_client.c +++ b/impl/test/layer2_over_udp/l2udptest_client.c @@ -50,7 +50,7 @@ static bool m_running = true; static int m_bcast_sock = -1; -static double next_tx_switch_time = 0.0; +static uint64_t next_tx_switch_time = 0; static rx_stats_t m_rx_stats; @@ -69,7 +69,7 @@ static void signal_handler(int signal, siginfo_t *info, void *ctx) static void block_tx_for(unsigned offset_ms) { - next_tx_switch_time = get_hires_time() + (double)offset_ms * 0.001; + next_tx_switch_time = get_hires_time() + HRTIME_MS(offset_ms); } @@ -143,7 +143,7 @@ int main(int argc, char **argv) bool on_air = true; - srand((int)(get_hires_time() * 1e6)); + srand(get_hires_time()); // ** Initialize ** @@ -218,18 +218,18 @@ int main(int argc, char **argv) pfd_bcast.fd = m_bcast_sock; pfd_bcast.events = POLLIN; - double old = get_hires_time(); + uint64_t old = get_hires_time(); size_t total_bytes = 0; - double next_stats_print_time = old + 0.5; + uint64_t next_stats_print_time = old + HRTIME_MS(500); - double retransmit_time = 0.0; + uint64_t retransmit_time = 0; while(m_running) { - double now = get_hires_time(); + uint64_t now = get_hires_time(); - if(retransmit_time != 0.0 && now >= retransmit_time) { + if(retransmit_time != 0 && now >= retransmit_time) { LOG(LVL_INFO, "Retransmit triggered."); - retransmit_time = 0.0; + retransmit_time = 0; connection_restart_tx(&l2conn); } @@ -301,7 +301,7 @@ int main(int argc, char **argv) LOG(LVL_INFO, "TX -> RX"); on_air = false; - retransmit_time = get_hires_time() + 1.0 + 1.0 * rand() / RAND_MAX; + retransmit_time = get_hires_time() + HRTIME_SEC(1) + HRTIME_SEC(1.0 * rand() / RAND_MAX); block_tx_for(TX_SWITCH_BACKOFF_AFTER_RX_ON); } @@ -334,9 +334,9 @@ int main(int argc, char **argv) total_bytes += ret; - double new = get_hires_time(); + uint64_t new = get_hires_time(); if(new >= next_stats_print_time) { - double rate = total_bytes / (new - old); + double rate = total_bytes * 1e9 / (new - old); LOG(LVL_INFO, "\nEstimated rate: %.3f kB/s", rate / 1e3); LOG(LVL_INFO, "Receiver statistics:"); LOG(LVL_INFO, " Preambles found: %8zd", m_rx_stats.preambles_found); @@ -346,7 +346,7 @@ int main(int argc, char **argv) m_rx_stats.header_errors, m_rx_stats.header_errors * 100.0f / m_rx_stats.preambles_found); LOG(LVL_INFO, " Failed decodes: %8zd (%6.2f %%)", m_rx_stats.failed_decodes, m_rx_stats.failed_decodes * 100.0f / m_rx_stats.preambles_found); - next_stats_print_time += 0.5; + next_stats_print_time += HRTIME_MS(500); total_bytes = 0; old = new; diff --git a/impl/test/layer2_over_udp/l2udptest_digipeater.c b/impl/test/layer2_over_udp/l2udptest_digipeater.c index 82d7efd..1d53eab 100644 --- a/impl/test/layer2_over_udp/l2udptest_digipeater.c +++ b/impl/test/layer2_over_udp/l2udptest_digipeater.c @@ -50,7 +50,7 @@ static bool m_running = true; static int m_bcast_sock = -1; -static double next_tx_switch_time = 0.0; +static uint64_t next_tx_switch_time = 0; static rx_stats_t m_rx_stats; @@ -69,7 +69,7 @@ static void signal_handler(int signal, siginfo_t *info, void *ctx) static void block_tx_for(unsigned offset_ms) { - next_tx_switch_time = get_hires_time() + (double)offset_ms * 0.001; + next_tx_switch_time = get_hires_time() + HRTIME_MS(offset_ms); } @@ -143,7 +143,7 @@ int main(int argc, char **argv) bool on_air = true; - srand((int)(get_hires_time() * 1e6)); + srand(get_hires_time()); // ** Initialize ** @@ -218,18 +218,18 @@ int main(int argc, char **argv) pfd_bcast.fd = m_bcast_sock; pfd_bcast.events = POLLIN; - double old = get_hires_time(); + uint64_t old = get_hires_time(); size_t total_bytes = 0; - double next_stats_print_time = old + 0.5; + uint64_t next_stats_print_time = old + HRTIME_MS(500); - double retransmit_time = 0.0; + uint64_t retransmit_time = 0; while(m_running) { - double now = get_hires_time(); + uint64_t now = get_hires_time(); - if(retransmit_time != 0.0 && now >= retransmit_time) { + if(retransmit_time != 0 && now >= retransmit_time) { LOG(LVL_INFO, "Retransmit triggered."); - retransmit_time = 0.0; + retransmit_time = 0; connection_restart_tx(&l2conn); } @@ -301,7 +301,7 @@ int main(int argc, char **argv) LOG(LVL_INFO, "TX -> RX"); on_air = false; - retransmit_time = get_hires_time() + 1.0 + 1.0 * rand() / RAND_MAX; + retransmit_time = get_hires_time() + HRTIME_SEC(1) + HRTIME_SEC(1.0 * rand() / RAND_MAX); block_tx_for(TX_SWITCH_BACKOFF_AFTER_RX_ON); } @@ -334,9 +334,9 @@ int main(int argc, char **argv) total_bytes += ret; - double new = get_hires_time(); + uint64_t new = get_hires_time(); if(new >= next_stats_print_time) { - double rate = total_bytes / (new - old); + double rate = total_bytes * 1e9 / (new - old); LOG(LVL_INFO, "\nEstimated rate: %.3f kB/s", rate / 1e3); LOG(LVL_INFO, "Receiver statistics:"); LOG(LVL_INFO, " Preambles found: %8zd", m_rx_stats.preambles_found); @@ -346,7 +346,7 @@ int main(int argc, char **argv) m_rx_stats.header_errors, m_rx_stats.header_errors * 100.0f / m_rx_stats.preambles_found); LOG(LVL_INFO, " Failed decodes: %8zd (%6.2f %%)", m_rx_stats.failed_decodes, m_rx_stats.failed_decodes * 100.0f / m_rx_stats.preambles_found); - next_stats_print_time += 0.5; + next_stats_print_time += HRTIME_MS(500); total_bytes = 0; old = new;