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.
This commit is contained in:
Olaf Bergmann committed 2015-02-21 16:34:10 +01:00
1 parent a81a37b29d
commit b6c92bb927
2 files changed
+69 -2

No files matched your search

+5
View File
@@ -1,3 +1,8 @@
2015-02-21 Olaf Bergmann <bergmann@tzi.org>
* net.c (coap_send_confirmed): Use fixed point arithmetic
to calculate retransmission timeout.
2015-02-20 Olaf Bergmann <bergmann@tzi.org>
* coap_list.[hc]: Moved old list implementation into
+64 -2
View File
@@ -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));