From b6c92bb9274d67fa5da20b3b7139ec0549c4be98 Mon Sep 17 00:00:00 2001 From: Olaf Bergmann Date: Sat, 21 Feb 2015 16:34:10 +0100 Subject: [PATCH] use fixpoint arithmetic for retransmission timeout calculation It was noted on the mailing list that the changed retransmission timeout calculation in commit 1a28b3 may yield to inefficient code on embedded systems which have no fpu. This change now uses fixed point arithmetic instead. By default, it represents ACK_TIMEOUT and ACK_RANDOM_FACTOR as Q2.6 numbers and uses Q0.8 for the randomness parameter. The resulting timeout value is represented in system ticks (i.e. a multiple of COAP_TICKS_PER_SECOND) as an unsigned integer. --- ChangeLog | 5 +++++ net.c | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 69 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index b34abf80..0b720b09 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2015-02-21 Olaf Bergmann + + * net.c (coap_send_confirmed): Use fixed point arithmetic + to calculate retransmission timeout. + 2015-02-20 Olaf Bergmann * coap_list.[hc]: Moved old list implementation into diff --git a/net.c b/net.c index 68261f72..eba6bd2c 100644 --- a/net.c +++ b/net.c @@ -87,6 +87,32 @@ /** @} */ +/** + * The number of bits for the fractional part of ACK_TIMEOUT and + * ACK_RANDOM_FACTOR. Must be less or equal 8. + */ +#define FRAC_BITS 6 + +/** + * The maximum number of bits for fixed point integers that are used + * for retransmission time calculation. Currently this must be @c 8. + */ +#define MAX_BITS 8 + +#if FRAC_BITS > 8 +#error FRAC_BITS must be less or equal 8 +#endif + +/** creates a Qx.frac from fval */ +#define Q(frac,fval) ((unsigned short)(((1 << (frac)) * (fval)))) + +/** creates a Qx.FRAC_BITS from COAP_DEFAULT_ACK_RANDOM_FACTOR */ +#define ACK_RANDOM_FACTOR \ + Q(FRAC_BITS, COAP_DEFAULT_ACK_RANDOM_FACTOR) + +/** creates a Qx.FRAC_BITS from COAP_DEFAULT_ACK_TIMEOUT */ +#define ACK_TIMEOUT Q(FRAC_BITS, COAP_DEFAULT_ACK_TIMEOUT) + #if defined(WITH_POSIX) time_t clock_offset; @@ -643,6 +669,43 @@ coap_send_message_type(coap_context_t *context, return result; } +/** + * Calculates the initial timeout based on the global CoAP transmission + * parameters ACK_TIMEOUT, ACK_RANDOM_FACTOR, and COAP_TICKS_PER_SECOND. + * The calculation requires ACK_TIMEOUT and ACK_RANDOM_FACTOR to be in + * Qx.FRAC_BITS fixed point notation, whereas the passed parameter @p r + * is interpreted as the fractional part of a Q0.MAX_BITS random value. + * + * @param r random value as fractional part of a Q0.MAX_BITS fixed point + * value + * @return COAP_TICKS_PER_SECOND * ACK_TIMEOUT * (1 + (ACK_RANDOM_FACTOR - 1) * r) + */ +static inline unsigned int +calc_timeout(unsigned char r) { + unsigned int result; + + /* The integer 1.0 as a Qx.FRAC_BITS */ +#define FP1 Q(FRAC_BITS, 1) + + /* rounds val up and right shifts by frac positions */ +#define SHR_FP(val,frac) (((val) + (1 << ((frac) - 1))) >> (frac)) + + /* Inner term: multiply ACK_RANDOM_FACTOR by Q0.MAX_BITS[r] and + * make the result a rounded Qx.FRAC_BITS */ + result = SHR_FP((ACK_RANDOM_FACTOR - FP1) * r, MAX_BITS); + + /* Add 1 to the inner term and multiply with ACK_TIMEOUT, then + * make the result a rounded Qx.FRAC_BITS */ + result = SHR_FP(((result + FP1) * ACK_TIMEOUT), FRAC_BITS); + + /* Multiply with COAP_TICKS_PER_SECOND to yield system ticks + * (yields a Qx.FRAC_BITS) and shift to get an integer */ + return SHR_FP((COAP_TICKS_PER_SECOND * result), FRAC_BITS); + +#undef FP1 +#undef SHR_FP +} + coap_tid_t coap_send_confirmed(coap_context_t *context, const coap_endpoint_t *local_interface, @@ -668,8 +731,7 @@ coap_send_confirmed(coap_context_t *context, prng((unsigned char *)&r,sizeof(r)); /* add timeout in range [ACK_TIMEOUT...ACK_TIMEOUT * ACK_RANDOM_FACTOR] */ - node->timeout = COAP_TICKS_PER_SECOND * COAP_DEFAULT_ACK_TIMEOUT * - (1 + (COAP_DEFAULT_ACK_RANDOM_FACTOR - 1) * ((r & 0xFF)/ 256.0)); + node->timeout = calc_timeout(r); node->local_if = *local_interface; memcpy(&node->remote, dst, sizeof(coap_address_t));