Contiki-NG
tsch-stats.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2016-2017, University of Bristol - http://www.bristol.ac.uk
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  * notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  * notice, this list of conditions and the following disclaimer in the
11  * documentation and/or other materials provided with the distribution.
12  * 3. Neither the name of the copyright holder nor the names of its
13  * contributors may be used to endorse or promote products derived
14  * from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  *
28  */
29 
30 /**
31  * \file
32  * Source file for TSCH statistics
33  * \author
34  * Atis Elsts <atis.elsts@bristol.ac.uk>
35  */
36 
37 #include "contiki.h"
38 #include "net/mac/tsch/tsch.h"
39 #include "net/netstack.h"
40 #include "dev/radio.h"
41 
42 /* Log configuration */
43 #include "sys/log.h"
44 #define LOG_MODULE "TSCH Stats"
45 #define LOG_LEVEL LOG_LEVEL_MAC
46 
47 /*---------------------------------------------------------------------------*/
48 #if TSCH_STATS_ON
49 /*---------------------------------------------------------------------------*/
50 
51 struct tsch_global_stats tsch_stats;
52 struct tsch_neighbor_stats tsch_neighbor_stats;
53 
54 /* Called every TSCH_STATS_DECAY_INTERVAL ticks */
55 static struct ctimer periodic_timer;
56 
57 static void periodic(void *);
58 
59 /*---------------------------------------------------------------------------*/
60 void
61 tsch_stats_init(void)
62 {
63 #if TSCH_STATS_SAMPLE_NOISE_RSSI
64  int i;
65 
66  for(i = 0; i < TSCH_STATS_NUM_CHANNELS; ++i) {
67  tsch_stats.noise_rssi[i] = TSCH_STATS_DEFAULT_RSSI;
68  tsch_stats.channel_free_ewma[i] = TSCH_STATS_DEFAULT_CHANNEL_FREE;
69  }
70 #endif
71 
72  tsch_stats_reset_neighbor_stats();
73 
74  /* Start the periodic processing soonish */
75  ctimer_set(&periodic_timer, TSCH_STATS_DECAY_INTERVAL / 10, periodic, NULL);
76 }
77 /*---------------------------------------------------------------------------*/
78 void
79 tsch_stats_reset_neighbor_stats(void)
80 {
81  int i;
82  struct tsch_channel_stats *ch_stats;
83 
84  ch_stats = tsch_neighbor_stats.channel_stats;
85  for(i = 0; i < TSCH_STATS_NUM_CHANNELS; ++i) {
86  ch_stats[i].rssi = TSCH_STATS_DEFAULT_RSSI;
87  ch_stats[i].lqi = TSCH_STATS_DEFAULT_LQI;
88  ch_stats[i].p_tx_success = TSCH_STATS_DEFAULT_P_TX;
89  }
90 }
91 /*---------------------------------------------------------------------------*/
92 struct tsch_neighbor_stats *
93 tsch_stats_get_from_neighbor(struct tsch_neighbor *n)
94 {
95  /* Due to RAM limitations, this module only collects neighbor stats about the time source */
96  if(n != NULL && n->is_time_source) {
97  return &tsch_neighbor_stats;
98  }
99  return NULL;
100 }
101 /*---------------------------------------------------------------------------*/
102 void
103 tsch_stats_tx_packet(struct tsch_neighbor *n, uint8_t mac_status, uint8_t channel)
104 {
105  struct tsch_neighbor_stats *stats;
106 
107  stats = tsch_stats_get_from_neighbor(n);
108  if(stats != NULL) {
109  uint8_t index = tsch_stats_channel_to_index(channel);
110  uint16_t new_tx_value = (mac_status == MAC_TX_OK ? 1 : 0);
111  new_tx_value *= TSCH_STATS_BINARY_SCALING_FACTOR;
112  TSCH_STATS_EWMA_UPDATE(stats->channel_stats[index].p_tx_success, new_tx_value);
113  }
114 }
115 /*---------------------------------------------------------------------------*/
116 void
117 tsch_stats_rx_packet(struct tsch_neighbor *n, int8_t rssi, uint8_t lqi, uint8_t channel)
118 {
119  struct tsch_neighbor_stats *stats;
120 
121  stats = tsch_stats_get_from_neighbor(n);
122  if(stats != NULL) {
123  uint8_t index = tsch_stats_channel_to_index(channel);
124 
125  TSCH_STATS_EWMA_UPDATE(stats->channel_stats[index].rssi,
126  TSCH_STATS_TRANSFORM(rssi, TSCH_STATS_RSSI_SCALING_FACTOR));
127  TSCH_STATS_EWMA_UPDATE(stats->channel_stats[index].lqi,
128  TSCH_STATS_TRANSFORM(lqi, TSCH_STATS_LQI_SCALING_FACTOR));
129  }
130 }
131 /*---------------------------------------------------------------------------*/
132 void
133 tsch_stats_on_time_synchronization(int32_t sync_error)
134 {
135  /* Update the maximal error so far if the absolute value of the new one is larger */
136  tsch_stats.max_sync_error = MAX(tsch_stats.max_sync_error, ABS(sync_error));
137 }
138 /*---------------------------------------------------------------------------*/
139 void
140 tsch_stats_sample_rssi(void)
141 {
142 #if TSCH_STATS_SAMPLE_NOISE_RSSI
143  uint8_t index;
144  radio_value_t value;
145  radio_result_t rv;
146 
147  static uint8_t measurement_channel = TSCH_STATS_FIRST_CHANNEL;
148 
149  index = tsch_stats_channel_to_index(measurement_channel);
150 
151  /* Select the measurement channel */
152  NETSTACK_RADIO.set_value(RADIO_PARAM_CHANNEL, measurement_channel);
153 
154  /* Need to explicitly turn on for Coojamotes */
155  NETSTACK_RADIO.on();
156 
157  /* Measure the background noise RSSI and act on it */
158  rv = NETSTACK_RADIO.get_value(RADIO_PARAM_RSSI, &value);
159  if(rv == RADIO_RESULT_OK) {
160  tsch_stat_t prev_busyness_metric;
161  uint16_t is_free;
162 
163  is_free = (((int)value <= TSCH_STATS_BUSY_CHANNEL_RSSI) ? 1 : 0) * TSCH_STATS_BINARY_SCALING_FACTOR;
164 
165  /* LOG_DBG("noise RSSI on %u: %d\n", measurement_channel, (int)value); */
166 
167  TSCH_STATS_EWMA_UPDATE(tsch_stats.noise_rssi[index],
168  TSCH_STATS_TRANSFORM((int)value, TSCH_STATS_RSSI_SCALING_FACTOR));
169 
170  prev_busyness_metric = tsch_stats.channel_free_ewma[index];
171  (void)prev_busyness_metric;
172  TSCH_STATS_EWMA_UPDATE(tsch_stats.channel_free_ewma[index], is_free);
173 
174  /* potentially select a new TSCH hopping sequence */
175 #ifdef TSCH_CALLBACK_CHANNEL_STATS_UPDATED
176  TSCH_CALLBACK_CHANNEL_STATS_UPDATED(measurement_channel, prev_busyness_metric);
177 #endif
178  } else {
179  LOG_ERR("! sampling RSSI failed: %d\n", (int)rv);
180  }
181 
182  /* Increment the channel index for the next time */
183  measurement_channel++;
184  if(measurement_channel >= TSCH_STATS_FIRST_CHANNEL + TSCH_STATS_NUM_CHANNELS) {
185  measurement_channel = TSCH_STATS_FIRST_CHANNEL;
186  }
187 #endif /* TSCH_STATS_SAMPLE_NOISE_RSSI */
188 }
189 /*---------------------------------------------------------------------------*/
190 /* Periodic timer called every TSCH_STATS_DECAY_INTERVAL ticks */
191 static void
192 periodic(void *ptr)
193 {
194  int i;
195  struct tsch_neighbor *timesource;
196  struct tsch_channel_stats *stats = tsch_neighbor_stats.channel_stats;
197 
198 #if TSCH_STATS_SAMPLE_NOISE_RSSI
199  LOG_DBG("Noise RSSI:\n");
200  for(i = 0; i < TSCH_STATS_NUM_CHANNELS; ++i) {
201  LOG_DBG(" channel %u: %d rssi, %u/%u free\n",
202  TSCH_STATS_FIRST_CHANNEL + i,
203  tsch_stats.noise_rssi[i] / TSCH_STATS_RSSI_SCALING_FACTOR,
204  tsch_stats.channel_free_ewma[i],
205  TSCH_STATS_BINARY_SCALING_FACTOR);
206  }
207 #endif
208 
209  timesource = tsch_queue_get_time_source();
210  if(timesource != NULL) {
211  LOG_DBG("Time source neighbor:\n");
212 
213  for(i = 0; i < TSCH_STATS_NUM_CHANNELS; ++i) {
214  LOG_DBG(" channel %u: %d rssi, %u lqi, %u/%u P(tx)\n",
215  TSCH_STATS_FIRST_CHANNEL + i,
216  stats[i].rssi / TSCH_STATS_RSSI_SCALING_FACTOR,
217  stats[i].lqi / TSCH_STATS_LQI_SCALING_FACTOR,
218  stats[i].p_tx_success,
219  TSCH_STATS_BINARY_SCALING_FACTOR);
220  }
221  }
222 
223  /* Do not decay the periodic global stats, as they are updated independely of packet rate */
224  for(i = 0; i < TSCH_STATS_NUM_CHANNELS; ++i) {
225  /* decay Rx stats */
226  TSCH_STATS_EWMA_UPDATE(stats[i].rssi, TSCH_STATS_DEFAULT_RSSI);
227  TSCH_STATS_EWMA_UPDATE(stats[i].lqi, TSCH_STATS_DEFAULT_LQI);
228  /* decay Tx stats */
229  TSCH_STATS_EWMA_UPDATE(stats[i].p_tx_success, TSCH_STATS_DEFAULT_P_TX);
230  }
231 
232  ctimer_set(&periodic_timer, TSCH_STATS_DECAY_INTERVAL, periodic, NULL);
233 }
234 /*---------------------------------------------------------------------------*/
235 #endif /* TSCH_STATS_ON */
236 /*---------------------------------------------------------------------------*/
Header file for the radio API
TSCH neighbor information.
Definition: tsch-types.h:109
struct tsch_neighbor * tsch_queue_get_time_source(void)
Get the TSCH time source (we currently assume there is only one)
Definition: tsch-queue.c:124
int radio_value_t
Each radio has a set of parameters that designate the current configuration and state of the radio...
Definition: radio.h:88
The MAC layer transmission was OK.
Definition: mac.h:87
void ctimer_set(struct ctimer *c, clock_time_t t, void(*f)(void *), void *ptr)
Set a callback timer.
Definition: ctimer.c:99
Main API declarations for TSCH.
Include file for the Contiki low-layer network stack (NETSTACK)
Header file for the logging system