Link-stats: new option to compute ETX from Tx and ACK count. More stable than EWMA.
This commit is contained in:
parent
ac4558bea0
commit
a791f46002
@ -44,43 +44,36 @@
|
||||
#define PRINTF(...)
|
||||
#endif
|
||||
|
||||
/* Half time for the freshness counter, in minutes */
|
||||
#define FRESHNESS_HALF_LIFE 15
|
||||
/* Maximum value for the Tx count counter */
|
||||
#define TX_COUNT_MAX 32
|
||||
|
||||
/* Statistics with no update in FRESHNESS_EXPIRATION_TIMEOUT is not fresh */
|
||||
#define FRESHNESS_EXPIRATION_TIME (10 * 60 * (clock_time_t)CLOCK_SECOND)
|
||||
/* Half time for the freshness counter */
|
||||
#define FRESHNESS_HALF_LIFE (15 * 60 * (clock_time_t)CLOCK_SECOND)
|
||||
/* Statistics are fresh if the freshness counter is FRESHNESS_TARGET or more */
|
||||
#define FRESHNESS_TARGET 4
|
||||
/* Maximum value for the freshness counter */
|
||||
#define FRESHNESS_MAX 16
|
||||
/* Statistics with no update in FRESHNESS_EXPIRATION_TIMEOUT is not fresh */
|
||||
#define FRESHNESS_EXPIRATION_TIME (10 * 60 * (clock_time_t)CLOCK_SECOND)
|
||||
|
||||
/* EWMA (exponential moving average) used to maintain statistics over time */
|
||||
#define EWMA_SCALE 100
|
||||
#define EWMA_ALPHA 10
|
||||
#define EWMA_BOOTSTRAP_ALPHA 25
|
||||
#define EWMA_SCALE 100
|
||||
#define EWMA_ALPHA 10
|
||||
#define EWMA_BOOTSTRAP_ALPHA 25
|
||||
|
||||
/* ETX fixed point divisor. 128 is the value used by RPL (RFC 6551 and RFC 6719) */
|
||||
#define ETX_DIVISOR LINK_STATS_ETX_DIVISOR
|
||||
#define ETX_DIVISOR LINK_STATS_ETX_DIVISOR
|
||||
/* Number of Tx used to update the ETX EWMA in case of no-ACK */
|
||||
#define ETX_NOACK_PENALTY 24
|
||||
#define ETX_NOACK_PENALTY 16
|
||||
/* Initial ETX value */
|
||||
#define ETX_DEFAULT 2
|
||||
#define ETX_DEFAULT 2
|
||||
|
||||
/* Per-neighbor link statistics table */
|
||||
NBR_TABLE(struct link_stats, link_stats);
|
||||
|
||||
/* Called every FRESHNESS_HALF_LIFE minutes */
|
||||
/* Called at a period of FRESHNESS_HALF_LIFE */
|
||||
struct ctimer periodic_timer;
|
||||
|
||||
/* Used to initialize ETX before any transmission occurs. By default,
|
||||
* infer the initial ETX from the RSSI of previously received packets.
|
||||
* To use a statuc value of e.g. ETX_DEFAULT, use:
|
||||
* #define LINK_STATS_CONF_INIT_ETX(stats) (ETX_DEFAULT * ETX_DIVISOR) */
|
||||
#ifdef LINK_STATS_CONF_INIT_ETX
|
||||
#define LINK_STATS_INIT_ETX(stats) LINK_STATS_CONF_INIT_ETX(stats)
|
||||
#else /* LINK_STATS_INIT_ETX */
|
||||
#define LINK_STATS_INIT_ETX(stats) guess_etx_from_rssi(stats)
|
||||
#endif /* LINK_STATS_INIT_ETX */
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Returns the neighbor's link stats */
|
||||
const struct link_stats *
|
||||
@ -98,6 +91,7 @@ link_stats_is_fresh(const struct link_stats *stats)
|
||||
&& stats->freshness >= FRESHNESS_TARGET;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
#if LINK_STATS_INIT_ETX_FROM_RSSI
|
||||
uint16_t
|
||||
guess_etx_from_rssi(const struct link_stats *stats)
|
||||
{
|
||||
@ -126,14 +120,17 @@ guess_etx_from_rssi(const struct link_stats *stats)
|
||||
}
|
||||
return 0xffff;
|
||||
}
|
||||
#endif /* LINK_STATS_INIT_ETX_FROM_RSSI */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Packet sent callback. Updates stats for transmissions to lladdr */
|
||||
void
|
||||
link_stats_packet_sent(const linkaddr_t *lladdr, int status, int numtx)
|
||||
{
|
||||
struct link_stats *stats;
|
||||
#if !LINK_STATS_ETX_FROM_PACKET_COUNT
|
||||
uint16_t packet_etx;
|
||||
uint8_t ewma_alpha;
|
||||
#endif /* !LINK_STATS_ETX_FROM_PACKET_COUNT */
|
||||
|
||||
if(status != MAC_TX_OK && status != MAC_TX_NOACK) {
|
||||
/* Do not penalize the ETX when collisions or transmission errors occur. */
|
||||
@ -145,7 +142,11 @@ link_stats_packet_sent(const linkaddr_t *lladdr, int status, int numtx)
|
||||
/* Add the neighbor */
|
||||
stats = nbr_table_add_lladdr(link_stats, lladdr, NBR_TABLE_REASON_LINK_STATS, NULL);
|
||||
if(stats != NULL) {
|
||||
stats->etx = LINK_STATS_INIT_ETX(stats);
|
||||
#if LINK_STATS_INIT_ETX_FROM_RSSI
|
||||
stats->etx = guess_etx_from_rssi(stats);
|
||||
#else /* LINK_STATS_INIT_ETX_FROM_RSSI */
|
||||
stats->etx = ETX_DEFAULT * ETX_DIVISOR;
|
||||
#endif /* LINK_STATS_INIT_ETX_FROM_RSSI */
|
||||
} else {
|
||||
return; /* No space left, return */
|
||||
}
|
||||
@ -155,6 +156,28 @@ link_stats_packet_sent(const linkaddr_t *lladdr, int status, int numtx)
|
||||
stats->last_tx_time = clock_time();
|
||||
stats->freshness = MIN(stats->freshness + numtx, FRESHNESS_MAX);
|
||||
|
||||
#if LINK_STATS_ETX_FROM_PACKET_COUNT
|
||||
/* Compute ETX from packet and ACK count */
|
||||
numtx = (status == MAC_TX_NOACK) ? ETX_NOACK_PENALTY : numtx;
|
||||
/* Halve both counter after TX_COUNT_MAX */
|
||||
if(stats->tx_count + numtx > TX_COUNT_MAX) {
|
||||
stats->tx_count /= 2;
|
||||
stats->ack_count /= 2;
|
||||
}
|
||||
/* Update tx_count and ack_count */
|
||||
stats->tx_count += numtx;
|
||||
if(status == MAC_TX_OK) {
|
||||
stats->ack_count++;
|
||||
}
|
||||
/* Compute ETX */
|
||||
if(stats->ack_count > 0) {
|
||||
stats->etx = ((uint16_t)stats->tx_count * ETX_DIVISOR) / stats->ack_count;
|
||||
} else {
|
||||
stats->etx = (uint16_t)MAX(ETX_NOACK_PENALTY, stats->tx_count) * ETX_DIVISOR;
|
||||
}
|
||||
#else /* LINK_STATS_ETX_FROM_PACKET_COUNT */
|
||||
/* Compute ETX using an EWMA */
|
||||
|
||||
/* ETX used for this update */
|
||||
packet_etx = ((status == MAC_TX_NOACK) ? ETX_NOACK_PENALTY : numtx) * ETX_DIVISOR;
|
||||
/* ETX alpha used for this update */
|
||||
@ -163,6 +186,7 @@ link_stats_packet_sent(const linkaddr_t *lladdr, int status, int numtx)
|
||||
/* Compute EWMA and update ETX */
|
||||
stats->etx = ((uint32_t)stats->etx * (EWMA_SCALE - ewma_alpha) +
|
||||
(uint32_t)packet_etx * ewma_alpha) / EWMA_SCALE;
|
||||
#endif /* LINK_STATS_ETX_FROM_PACKET_COUNT */
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Packet input callback. Updates statistics for receptions on a given link */
|
||||
@ -179,7 +203,11 @@ link_stats_input_callback(const linkaddr_t *lladdr)
|
||||
if(stats != NULL) {
|
||||
/* Initialize */
|
||||
stats->rssi = packet_rssi;
|
||||
stats->etx = LINK_STATS_INIT_ETX(stats);
|
||||
#if LINK_STATS_INIT_ETX_FROM_RSSI
|
||||
stats->etx = guess_etx_from_rssi(stats);
|
||||
#else /* LINK_STATS_INIT_ETX_FROM_RSSI */
|
||||
stats->etx = ETX_DEFAULT * ETX_DIVISOR;
|
||||
#endif /* LINK_STATS_INIT_ETX_FROM_RSSI */
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -189,7 +217,7 @@ link_stats_input_callback(const linkaddr_t *lladdr)
|
||||
(int32_t)packet_rssi * EWMA_ALPHA) / EWMA_SCALE;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Periodic timer called every FRESHNESS_HALF_LIFE minutes */
|
||||
/* Periodic timer called at a period of FRESHNESS_HALF_LIFE */
|
||||
static void
|
||||
periodic(void *ptr)
|
||||
{
|
||||
@ -218,6 +246,5 @@ void
|
||||
link_stats_init(void)
|
||||
{
|
||||
nbr_table_register(link_stats, NULL);
|
||||
ctimer_set(&periodic_timer, 60 * (clock_time_t)CLOCK_SECOND * FRESHNESS_HALF_LIFE,
|
||||
periodic, NULL);
|
||||
ctimer_set(&periodic_timer, FRESHNESS_HALF_LIFE, periodic, NULL);
|
||||
}
|
||||
|
@ -37,17 +37,35 @@
|
||||
|
||||
/* ETX fixed point divisor. 128 is the value used by RPL (RFC 6551 and RFC 6719) */
|
||||
#ifdef LINK_STATS_CONF_ETX_DIVISOR
|
||||
#define LINK_STATS_ETX_DIVISOR LINK_STATS_CONF_ETX_DIVISOR
|
||||
#define LINK_STATS_ETX_DIVISOR LINK_STATS_CONF_ETX_DIVISOR
|
||||
#else /* LINK_STATS_CONF_ETX_DIVISOR */
|
||||
#define LINK_STATS_ETX_DIVISOR 128
|
||||
#define LINK_STATS_ETX_DIVISOR 128
|
||||
#endif /* LINK_STATS_CONF_ETX_DIVISOR */
|
||||
|
||||
/* Option to infer the initial ETX from the RSSI of previously received packets. */
|
||||
#ifdef LINK_STATS_CONF_INIT_ETX_FROM_RSSI
|
||||
#define LINK_STATS_INIT_ETX_FROM_RSSI LINK_STATS_CONF_INIT_ETX_FROM_RSSI
|
||||
#else /* LINK_STATS_CONF_INIT_ETX_FROM_RSSI */
|
||||
#define LINK_STATS_INIT_ETX_FROM_RSSI 1
|
||||
#endif /* LINK_STATS_CONF_INIT_ETX_FROM_RSSI */
|
||||
|
||||
/* Option to use packet and ACK count for ETX estimation, instead of EWMA */
|
||||
#ifdef LINK_STATS_CONF_ETX_FROM_PACKET_COUNT
|
||||
#define LINK_STATS_ETX_FROM_PACKET_COUNT LINK_STATS_CONF_ETX_FROM_PACKET_COUNT
|
||||
#else /* LINK_STATS_CONF_ETX_FROM_PACKET_COUNT */
|
||||
#define LINK_STATS_ETX_FROM_PACKET_COUNT 0
|
||||
#endif /* LINK_STATS_ETX_FROM_PACKET_COUNT */
|
||||
|
||||
/* All statistics of a given link */
|
||||
struct link_stats {
|
||||
clock_time_t last_tx_time; /* Last Tx timestamp */
|
||||
uint16_t etx; /* ETX using ETX_DIVISOR as fixed point divisor */
|
||||
int16_t rssi; /* RSSI (received signal strength) */
|
||||
uint8_t freshness; /* Freshness of the statistics */
|
||||
clock_time_t last_tx_time; /* Last Tx timestamp */
|
||||
#if LINK_STATS_ETX_FROM_PACKET_COUNT
|
||||
uint8_t tx_count; /* Tx count, used for ETX calculation */
|
||||
uint8_t ack_count; /* ACK count, used for ETX calculation */
|
||||
#endif /* LINK_STATS_ETX_FROM_PACKET_COUNT */
|
||||
};
|
||||
|
||||
/* Returns the neighbor's link statistics */
|
||||
|
Loading…
Reference in New Issue
Block a user