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
71NBR_TABLE(
struct link_stats, link_stats);
74struct ctimer periodic_timer;
78const struct link_stats *
79link_stats_from_lladdr(
const linkaddr_t *lladdr)
81 return nbr_table_get_from_lladdr(link_stats, lladdr);
86link_stats_get_lladdr(
const struct link_stats *stat)
88 return nbr_table_get_lladdr(link_stats, stat);
93link_stats_is_fresh(
const struct link_stats *stats)
95 return (stats != NULL)
96 &&
clock_time() - stats->last_tx_time < FRESHNESS_EXPIRATION_TIME
97 && stats->freshness >= FRESHNESS_TARGET;
100#if LINK_STATS_INIT_ETX_FROM_RSSI
102guess_etx_from_rssi(
const struct link_stats *stats)
105 if(stats->rssi == LINK_STATS_RSSI_UNKNOWN) {
106 return ETX_DEFAULT * ETX_DIVISOR;
115#define ETX_INIT_MAX 3
118#define RSSI_DIFF (RSSI_HIGH - RSSI_LOW)
120 int16_t bounded_rssi = stats->rssi;
121 bounded_rssi = MIN(bounded_rssi, RSSI_HIGH);
122 bounded_rssi = MAX(bounded_rssi, RSSI_LOW + 1);
123 etx = RSSI_DIFF * ETX_DIVISOR / (bounded_rssi - RSSI_LOW);
124 return MIN(etx, ETX_INIT_MAX * ETX_DIVISOR);
133link_stats_packet_sent(
const linkaddr_t *lladdr,
int status,
int numtx)
135 struct link_stats *stats;
136#if !LINK_STATS_ETX_FROM_PACKET_COUNT
146 stats = nbr_table_get_from_lladdr(link_stats, lladdr);
154 stats = nbr_table_add_lladdr(link_stats, lladdr, NBR_TABLE_REASON_LINK_STATS, NULL);
158 stats->rssi = LINK_STATS_RSSI_UNKNOWN;
161 if(status == MAC_TX_QUEUE_FULL) {
162#if LINK_STATS_PACKET_COUNTERS
163 stats->cnt_current.num_queue_drops += 1;
171 stats->freshness = MIN(stats->freshness + numtx, FRESHNESS_MAX);
173#if LINK_STATS_PACKET_COUNTERS
175 stats->cnt_current.num_packets_tx += numtx;
177 stats->cnt_current.num_packets_acked++;
183 numtx += ETX_NOACK_PENALTY;
186#if LINK_STATS_ETX_FROM_PACKET_COUNT
189 if(stats->tx_count + numtx > TX_COUNT_MAX) {
190 stats->tx_count /= 2;
191 stats->ack_count /= 2;
194 stats->tx_count += numtx;
199 if(stats->ack_count > 0) {
200 stats->etx = ((uint16_t)stats->tx_count * ETX_DIVISOR) / stats->ack_count;
202 stats->etx = (uint16_t)MAX(ETX_NOACK_PENALTY, stats->tx_count) * ETX_DIVISOR;
208 packet_etx = numtx * ETX_DIVISOR;
210 ewma_alpha = link_stats_is_fresh(stats) ? EWMA_ALPHA : EWMA_BOOTSTRAP_ALPHA;
212 if(stats->etx == 0) {
214 stats->etx = packet_etx;
217 stats->etx = ((uint32_t)stats->etx * (EWMA_SCALE - ewma_alpha) +
218 (uint32_t)packet_etx * ewma_alpha) / EWMA_SCALE;
225link_stats_input_callback(
const linkaddr_t *lladdr)
227 struct link_stats *stats;
228 int16_t packet_rssi = packetbuf_attr(PACKETBUF_ATTR_RSSI);
230 stats = nbr_table_get_from_lladdr(link_stats, lladdr);
233 stats = nbr_table_add_lladdr(link_stats, lladdr, NBR_TABLE_REASON_LINK_STATS, NULL);
237 stats->rssi = LINK_STATS_RSSI_UNKNOWN;
240 if(stats->rssi == LINK_STATS_RSSI_UNKNOWN) {
242 stats->rssi = packet_rssi;
245 stats->rssi = ((int32_t)stats->rssi * (EWMA_SCALE - EWMA_ALPHA) +
246 (int32_t)packet_rssi * EWMA_ALPHA) / EWMA_SCALE;
249 if(stats->etx == 0) {
251#if LINK_STATS_INIT_ETX_FROM_RSSI
252 stats->etx = guess_etx_from_rssi(stats);
254 stats->etx = ETX_DEFAULT * ETX_DIVISOR;
258#if LINK_STATS_PACKET_COUNTERS
259 stats->cnt_current.num_packets_rx++;
263#if LINK_STATS_PACKET_COUNTERS
266print_and_update_counters(
void)
268 struct link_stats *stats;
270 for(stats = nbr_table_head(link_stats); stats != NULL;
271 stats = nbr_table_next(link_stats, stats)) {
273 struct link_packet_counter *c = &stats->cnt_current;
275 LOG_INFO(
"num packets: tx=%u ack=%u rx=%u queue_drops=%u to=",
276 c->num_packets_tx, c->num_packets_acked,
277 c->num_packets_rx, c->num_queue_drops);
278 LOG_INFO_LLADDR(link_stats_get_lladdr(stats));
281 stats->cnt_total.num_packets_tx += stats->cnt_current.num_packets_tx;
282 stats->cnt_total.num_packets_acked += stats->cnt_current.num_packets_acked;
283 stats->cnt_total.num_packets_rx += stats->cnt_current.num_packets_rx;
284 stats->cnt_total.num_queue_drops += stats->cnt_current.num_queue_drops;
285 memset(&stats->cnt_current, 0,
sizeof(stats->cnt_current));
296 struct link_stats *stats;
298 for(stats = nbr_table_head(link_stats); stats != NULL; stats = nbr_table_next(link_stats, stats)) {
299 stats->freshness >>= 1;
302#if LINK_STATS_PACKET_COUNTERS
303 print_and_update_counters();
309link_stats_reset(
void)
311 struct link_stats *stats;
312 stats = nbr_table_head(link_stats);
313 while(stats != NULL) {
314 nbr_table_remove(link_stats, stats);
315 stats = nbr_table_next(link_stats, stats);
323 nbr_table_register(link_stats, NULL);
324 ctimer_set(&periodic_timer, FRESHNESS_HALF_LIFE, periodic, NULL);
clock_time_t clock_time(void)
Get the current clock time.
void ctimer_set(struct ctimer *c, clock_time_t t, void(*f)(void *), void *ptr)
Set a callback timer.
void ctimer_reset(struct ctimer *c)
Reset a callback timer with the same interval as was previously set.
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.