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);
153 stats = nbr_table_add_lladdr(link_stats, lladdr, NBR_TABLE_REASON_LINK_STATS, NULL);
155 #if LINK_STATS_INIT_ETX_FROM_RSSI 156 stats->etx = guess_etx_from_rssi(stats);
158 stats->etx = ETX_DEFAULT * ETX_DIVISOR;
167 stats->freshness = MIN(stats->freshness + numtx, FRESHNESS_MAX);
169 #if LINK_STATS_PACKET_COUNTERS 171 stats->cnt_current.num_packets_tx += numtx;
173 stats->cnt_current.num_packets_acked++;
179 numtx += ETX_NOACK_PENALTY;
182 #if LINK_STATS_ETX_FROM_PACKET_COUNT 185 if(stats->tx_count + numtx > TX_COUNT_MAX) {
186 stats->tx_count /= 2;
187 stats->ack_count /= 2;
190 stats->tx_count += numtx;
195 if(stats->ack_count > 0) {
196 stats->etx = ((uint16_t)stats->tx_count * ETX_DIVISOR) / stats->ack_count;
198 stats->etx = (uint16_t)MAX(ETX_NOACK_PENALTY, stats->tx_count) * ETX_DIVISOR;
204 packet_etx = numtx * ETX_DIVISOR;
206 ewma_alpha = link_stats_is_fresh(stats) ? EWMA_ALPHA : EWMA_BOOTSTRAP_ALPHA;
209 stats->etx = ((uint32_t)stats->etx * (EWMA_SCALE - ewma_alpha) +
210 (uint32_t)packet_etx * ewma_alpha) / EWMA_SCALE;
216 link_stats_input_callback(
const linkaddr_t *lladdr)
218 struct link_stats *stats;
219 int16_t packet_rssi = packetbuf_attr(PACKETBUF_ATTR_RSSI);
221 stats = nbr_table_get_from_lladdr(link_stats, lladdr);
224 stats = nbr_table_add_lladdr(link_stats, lladdr, NBR_TABLE_REASON_LINK_STATS, NULL);
227 stats->rssi = packet_rssi;
228 #if LINK_STATS_INIT_ETX_FROM_RSSI 229 stats->etx = guess_etx_from_rssi(stats);
231 stats->etx = ETX_DEFAULT * ETX_DIVISOR;
233 #if LINK_STATS_PACKET_COUNTERS 234 stats->cnt_current.num_packets_rx = 1;
241 stats->rssi = ((int32_t)stats->rssi * (EWMA_SCALE - EWMA_ALPHA) +
242 (int32_t)packet_rssi * EWMA_ALPHA) / EWMA_SCALE;
244 #if LINK_STATS_PACKET_COUNTERS 245 stats->cnt_current.num_packets_rx++;
249 #if LINK_STATS_PACKET_COUNTERS 252 print_and_update_counters(
void)
254 struct link_stats *stats;
256 for(stats = nbr_table_head(link_stats); stats != NULL;
257 stats = nbr_table_next(link_stats, stats)) {
259 struct link_packet_counter *c = &stats->cnt_current;
261 LOG_INFO(
"num packets: tx=%u ack=%u rx=%u to=",
262 c->num_packets_tx, c->num_packets_acked, c->num_packets_rx);
263 LOG_INFO_LLADDR(link_stats_get_lladdr(stats));
266 stats->cnt_total.num_packets_tx += stats->cnt_current.num_packets_tx;
267 stats->cnt_total.num_packets_acked += stats->cnt_current.num_packets_acked;
268 stats->cnt_total.num_packets_rx += stats->cnt_current.num_packets_rx;
269 memset(&stats->cnt_current, 0,
sizeof(stats->cnt_current));
280 struct link_stats *stats;
282 for(stats = nbr_table_head(link_stats); stats != NULL; stats = nbr_table_next(link_stats, stats)) {
283 stats->freshness >>= 1;
286 #if LINK_STATS_PACKET_COUNTERS 287 print_and_update_counters();
293 link_stats_reset(
void)
295 struct link_stats *stats;
296 stats = nbr_table_head(link_stats);
297 while(stats != NULL) {
298 nbr_table_remove(link_stats, stats);
299 stats = nbr_table_next(link_stats, stats);
305 link_stats_init(
void)
307 nbr_table_register(link_stats, NULL);
308 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