34 #include "sys/clock.h" 36 #include "net/nbr-table.h" 37 #include "net/link-stats.h" 42 #define LOG_MODULE "Link Stats" 43 #define LOG_LEVEL LOG_LEVEL_MAC 46 #define TX_COUNT_MAX 32 49 #define FRESHNESS_EXPIRATION_TIME (10 * 60 * (clock_time_t)CLOCK_SECOND) 51 #define FRESHNESS_HALF_LIFE (15 * 60 * (clock_time_t)CLOCK_SECOND) 53 #define FRESHNESS_TARGET 4 55 #define FRESHNESS_MAX 16 58 #define EWMA_SCALE 100 60 #define EWMA_BOOTSTRAP_ALPHA 25 63 #define ETX_DIVISOR LINK_STATS_ETX_DIVISOR 65 #define ETX_NOACK_PENALTY 12 70 NBR_TABLE(
struct link_stats, link_stats);
73 struct ctimer periodic_timer;
77 const struct link_stats *
78 link_stats_from_lladdr(
const linkaddr_t *lladdr)
80 return nbr_table_get_from_lladdr(link_stats, lladdr);
85 link_stats_get_lladdr(
const struct link_stats *stat)
87 return nbr_table_get_lladdr(link_stats, stat);
92 link_stats_is_fresh(
const struct link_stats *stats)
94 return (stats != NULL)
95 &&
clock_time() - stats->last_tx_time < FRESHNESS_EXPIRATION_TIME
96 && stats->freshness >= FRESHNESS_TARGET;
99 #if LINK_STATS_INIT_ETX_FROM_RSSI 101 guess_etx_from_rssi(
const struct link_stats *stats)
104 if(stats->rssi == 0) {
105 return ETX_DEFAULT * ETX_DIVISOR;
114 #define ETX_INIT_MAX 3 115 #define RSSI_HIGH -60 117 #define RSSI_DIFF (RSSI_HIGH - RSSI_LOW) 119 int16_t bounded_rssi = stats->rssi;
120 bounded_rssi = MIN(bounded_rssi, RSSI_HIGH);
121 bounded_rssi = MAX(bounded_rssi, RSSI_LOW + 1);
122 etx = RSSI_DIFF * ETX_DIVISOR / (bounded_rssi - RSSI_LOW);
123 return MIN(etx, ETX_INIT_MAX * ETX_DIVISOR);
132 link_stats_packet_sent(
const linkaddr_t *lladdr,
int status,
int numtx)
134 struct link_stats *stats;
135 #if !LINK_STATS_ETX_FROM_PACKET_COUNT 145 stats = nbr_table_get_from_lladdr(link_stats, lladdr);
148 stats = nbr_table_add_lladdr(link_stats, lladdr, NBR_TABLE_REASON_LINK_STATS, NULL);
150 #if LINK_STATS_INIT_ETX_FROM_RSSI 151 stats->etx = guess_etx_from_rssi(stats);
153 stats->etx = ETX_DEFAULT * ETX_DIVISOR;
162 stats->freshness = MIN(stats->freshness + numtx, FRESHNESS_MAX);
164 #if LINK_STATS_PACKET_COUNTERS 166 stats->cnt_current.num_packets_tx += numtx;
168 stats->cnt_current.num_packets_acked++;
174 numtx += ETX_NOACK_PENALTY;
177 #if LINK_STATS_ETX_FROM_PACKET_COUNT 180 if(stats->tx_count + numtx > TX_COUNT_MAX) {
181 stats->tx_count /= 2;
182 stats->ack_count /= 2;
185 stats->tx_count += numtx;
190 if(stats->ack_count > 0) {
191 stats->etx = ((uint16_t)stats->tx_count * ETX_DIVISOR) / stats->ack_count;
193 stats->etx = (uint16_t)MAX(ETX_NOACK_PENALTY, stats->tx_count) * ETX_DIVISOR;
199 packet_etx = numtx * ETX_DIVISOR;
201 ewma_alpha = link_stats_is_fresh(stats) ? EWMA_ALPHA : EWMA_BOOTSTRAP_ALPHA;
204 stats->etx = ((uint32_t)stats->etx * (EWMA_SCALE - ewma_alpha) +
205 (uint32_t)packet_etx * ewma_alpha) / EWMA_SCALE;
211 link_stats_input_callback(
const linkaddr_t *lladdr)
213 struct link_stats *stats;
214 int16_t packet_rssi = packetbuf_attr(PACKETBUF_ATTR_RSSI);
216 stats = nbr_table_get_from_lladdr(link_stats, lladdr);
219 stats = nbr_table_add_lladdr(link_stats, lladdr, NBR_TABLE_REASON_LINK_STATS, NULL);
222 stats->rssi = packet_rssi;
223 #if LINK_STATS_INIT_ETX_FROM_RSSI 224 stats->etx = guess_etx_from_rssi(stats);
226 stats->etx = ETX_DEFAULT * ETX_DIVISOR;
233 stats->rssi = ((int32_t)stats->rssi * (EWMA_SCALE - EWMA_ALPHA) +
234 (int32_t)packet_rssi * EWMA_ALPHA) / EWMA_SCALE;
236 #if LINK_STATS_PACKET_COUNTERS 237 stats->cnt_current.num_packets_rx++;
241 #if LINK_STATS_PACKET_COUNTERS 244 print_and_update_counters(
void)
246 struct link_stats *stats;
248 for(stats = nbr_table_head(link_stats); stats != NULL;
249 stats = nbr_table_next(link_stats, stats)) {
251 struct link_packet_counter *c = &stats->cnt_current;
253 LOG_INFO(
"num packets: tx=%u ack=%u rx=%u to=",
254 c->num_packets_tx, c->num_packets_acked, c->num_packets_rx);
255 LOG_INFO_LLADDR(link_stats_get_lladdr(stats));
258 stats->cnt_total.num_packets_tx += stats->cnt_current.num_packets_tx;
259 stats->cnt_total.num_packets_acked += stats->cnt_current.num_packets_acked;
260 stats->cnt_total.num_packets_rx += stats->cnt_current.num_packets_rx;
261 memset(&stats->cnt_current, 0,
sizeof(stats->cnt_current));
272 struct link_stats *stats;
274 for(stats = nbr_table_head(link_stats); stats != NULL; stats = nbr_table_next(link_stats, stats)) {
275 stats->freshness >>= 1;
278 #if LINK_STATS_PACKET_COUNTERS 279 print_and_update_counters();
285 link_stats_reset(
void)
287 struct link_stats *stats;
288 stats = nbr_table_head(link_stats);
289 while(stats != NULL) {
290 nbr_table_remove(link_stats, stats);
291 stats = nbr_table_next(link_stats, stats);
297 link_stats_init(
void)
299 nbr_table_register(link_stats, NULL);
300 ctimer_set(&periodic_timer, FRESHNESS_HALF_LIFE, periodic, NULL);
void ctimer_reset(struct ctimer *c)
Reset a callback timer with the same interval as was previously set.
The MAC layer transmission was OK.
void ctimer_set(struct ctimer *c, clock_time_t t, void(*f)(void *), void *ptr)
Set a callback timer.
clock_time_t clock_time(void)
Get the current clock time.
The MAC layer deferred the transmission for a later time.
Header file for the Packet buffer (packetbuf) management
Header file for the logging system