37#include "net/nbr-table.h"
38#include "net/link-stats.h"
43#define LOG_MODULE "Link Stats"
44#define LOG_LEVEL LOG_LEVEL_MAC
47#define TX_COUNT_MAX 32
50#define FRESHNESS_EXPIRATION_TIME (10 * 60 * (clock_time_t)CLOCK_SECOND)
52#define FRESHNESS_HALF_LIFE (15 * 60 * (clock_time_t)CLOCK_SECOND)
54#define FRESHNESS_TARGET 4
56#define FRESHNESS_MAX 16
61#define EWMA_BOOTSTRAP_ALPHA 25
64#define ETX_DIVISOR LINK_STATS_ETX_DIVISOR
66#define ETX_NOACK_PENALTY 12
70#define RSSI_DIFF (LINK_STATS_RSSI_HIGH - LINK_STATS_RSSI_LOW)
74#error "RSSI_HIGH must be greater then RSSI_LOW"
78#if ETX_DIVISOR * RSSI_DIFF >= 0x10000
79#error "RSSI math overflow"
83NBR_TABLE(
struct link_stats, link_stats);
86struct ctimer periodic_timer;
90const struct link_stats *
91link_stats_from_lladdr(
const linkaddr_t *lladdr)
93 return nbr_table_get_from_lladdr(link_stats, lladdr);
98link_stats_get_lladdr(
const struct link_stats *stat)
100 return nbr_table_get_lladdr(link_stats, stat);
105link_stats_is_fresh(
const struct link_stats *stats)
107 return (stats != NULL)
108 &&
clock_time() - stats->last_tx_time < FRESHNESS_EXPIRATION_TIME
109 && stats->freshness >= FRESHNESS_TARGET;
112#if LINK_STATS_INIT_ETX_FROM_RSSI
120guess_etx_from_rssi(
const struct link_stats *stats)
123 if(stats->rssi == LINK_STATS_RSSI_UNKNOWN) {
124 return ETX_DEFAULT * ETX_DIVISOR;
126 const int16_t rssi_delta = stats->rssi - LINK_STATS_RSSI_LOW;
127 const int16_t bounded_rssi_delta = BOUND(rssi_delta, 0, RSSI_DIFF);
129 const uint16_t penalty = ETX_DIVISOR * bounded_rssi_delta / RSSI_DIFF;
131 const uint16_t etx = ETX_DIVISOR * ETX_DEFAULT + penalty;
132 return MIN(etx, LINK_STATS_ETX_INIT_MAX * ETX_DIVISOR);
141link_stats_packet_sent(
const linkaddr_t *lladdr,
int status,
int numtx)
143 struct link_stats *stats;
144#if !LINK_STATS_ETX_FROM_PACKET_COUNT
154 stats = nbr_table_get_from_lladdr(link_stats, lladdr);
162 stats = nbr_table_add_lladdr(link_stats, lladdr, NBR_TABLE_REASON_LINK_STATS, NULL);
166 stats->rssi = LINK_STATS_RSSI_UNKNOWN;
169 if(status == MAC_TX_QUEUE_FULL) {
170#if LINK_STATS_PACKET_COUNTERS
171 stats->cnt_current.num_queue_drops += 1;
179 stats->freshness = MIN(stats->freshness + numtx, FRESHNESS_MAX);
181#if LINK_STATS_PACKET_COUNTERS
183 stats->cnt_current.num_packets_tx += numtx;
185 stats->cnt_current.num_packets_acked++;
191 numtx += ETX_NOACK_PENALTY;
194#if LINK_STATS_ETX_FROM_PACKET_COUNT
197 if(stats->tx_count + numtx > TX_COUNT_MAX) {
198 stats->tx_count /= 2;
199 stats->ack_count /= 2;
202 stats->tx_count += numtx;
207 if(stats->ack_count > 0) {
208 stats->etx = ((uint16_t)stats->tx_count * ETX_DIVISOR) / stats->ack_count;
210 stats->etx = (uint16_t)MAX(ETX_NOACK_PENALTY, stats->tx_count) * ETX_DIVISOR;
216 packet_etx = numtx * ETX_DIVISOR;
218 ewma_alpha = link_stats_is_fresh(stats) ? EWMA_ALPHA : EWMA_BOOTSTRAP_ALPHA;
220 if(stats->etx == 0) {
222 stats->etx = packet_etx;
225 stats->etx = ((uint32_t)stats->etx * (EWMA_SCALE - ewma_alpha) +
226 (uint32_t)packet_etx * ewma_alpha) / EWMA_SCALE;
233link_stats_input_callback(
const linkaddr_t *lladdr)
235 struct link_stats *stats;
236 int16_t packet_rssi = packetbuf_attr(PACKETBUF_ATTR_RSSI);
238 stats = nbr_table_get_from_lladdr(link_stats, lladdr);
241 stats = nbr_table_add_lladdr(link_stats, lladdr, NBR_TABLE_REASON_LINK_STATS, NULL);
245 stats->rssi = LINK_STATS_RSSI_UNKNOWN;
248 if(stats->rssi == LINK_STATS_RSSI_UNKNOWN) {
250 stats->rssi = packet_rssi;
253 stats->rssi = ((int32_t)stats->rssi * (EWMA_SCALE - EWMA_ALPHA) +
254 (int32_t)packet_rssi * EWMA_ALPHA) / EWMA_SCALE;
257 if(stats->etx == 0) {
259#if LINK_STATS_INIT_ETX_FROM_RSSI
260 stats->etx = guess_etx_from_rssi(stats);
262 stats->etx = ETX_DEFAULT * ETX_DIVISOR;
266#if LINK_STATS_PACKET_COUNTERS
267 stats->cnt_current.num_packets_rx++;
271#if LINK_STATS_PACKET_COUNTERS
274print_and_update_counters(
void)
276 struct link_stats *stats;
278 for(stats = nbr_table_head(link_stats); stats != NULL;
279 stats = nbr_table_next(link_stats, stats)) {
281 struct link_packet_counter *c = &stats->cnt_current;
283 LOG_INFO(
"num packets: tx=%u ack=%u rx=%u queue_drops=%u to=",
284 c->num_packets_tx, c->num_packets_acked,
285 c->num_packets_rx, c->num_queue_drops);
286 LOG_INFO_LLADDR(link_stats_get_lladdr(stats));
289 stats->cnt_total.num_packets_tx += stats->cnt_current.num_packets_tx;
290 stats->cnt_total.num_packets_acked += stats->cnt_current.num_packets_acked;
291 stats->cnt_total.num_packets_rx += stats->cnt_current.num_packets_rx;
292 stats->cnt_total.num_queue_drops += stats->cnt_current.num_queue_drops;
293 memset(&stats->cnt_current, 0,
sizeof(stats->cnt_current));
304 struct link_stats *stats;
306 for(stats = nbr_table_head(link_stats); stats != NULL; stats = nbr_table_next(link_stats, stats)) {
307 stats->freshness >>= 1;
310#if LINK_STATS_PACKET_COUNTERS
311 print_and_update_counters();
317link_stats_reset(
void)
319 struct link_stats *stats;
320 stats = nbr_table_head(link_stats);
321 while(stats != NULL) {
322 nbr_table_remove(link_stats, stats);
323 stats = nbr_table_next(link_stats, stats);
331 nbr_table_register(link_stats, NULL);
332 ctimer_set(&periodic_timer, FRESHNESS_HALF_LIFE, periodic, NULL);
clock_time_t clock_time(void)
Get the current clock time.
void ctimer_reset(struct ctimer *c)
Reset a callback timer with the same interval as was previously set.
static void ctimer_set(struct ctimer *c, clock_time_t t, void(*f)(void *), void *ptr)
Set a callback timer.
Header file for the logging system.
@ MAC_TX_OK
The MAC layer transmission was OK.
@ MAC_TX_NOACK
The MAC layer deferred the transmission for a later time.
Header file for the Packet buffer (packetbuf) management.