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
51struct tsch_global_stats tsch_stats;
52struct tsch_neighbor_stats tsch_neighbor_stats;
53
54/* Called every TSCH_STATS_DECAY_INTERVAL ticks */
55static struct ctimer periodic_timer;
56
57static void periodic(void *);
58
59/*---------------------------------------------------------------------------*/
60void
61tsch_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/*---------------------------------------------------------------------------*/
78void
79tsch_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/*---------------------------------------------------------------------------*/
92struct tsch_neighbor_stats *
93tsch_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/*---------------------------------------------------------------------------*/
102void
103tsch_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/*---------------------------------------------------------------------------*/
116void
117tsch_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/*---------------------------------------------------------------------------*/
132void
133tsch_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/*---------------------------------------------------------------------------*/
139void
140tsch_stats_sample_rssi(void)
141{
142#if TSCH_STATS_SAMPLE_NOISE_RSSI
143 uint8_t index;
144 radio_value_t value;
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 */
191static void
192periodic(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/*---------------------------------------------------------------------------*/
void ctimer_set(struct ctimer *c, clock_time_t t, void(*f)(void *), void *ptr)
Set a callback timer.
Definition: ctimer.c:99
enum radio_result_e radio_result_t
Radio return values when setting or getting radio parameters.
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
@ RADIO_RESULT_OK
The parameter was set/read successfully.
Definition: radio.h:480
@ RADIO_PARAM_RSSI
Received signal strength indicator in dBm.
Definition: radio.h:218
@ RADIO_PARAM_CHANNEL
Channel used for radio communication.
Definition: radio.h:134
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:120
Header file for the logging system.
@ MAC_TX_OK
The MAC layer transmission was OK.
Definition: mac.h:87
Include file for the Contiki low-layer network stack (NETSTACK)
Header file for the radio API.
TSCH neighbor information.
Definition: tsch-types.h:109
Main API declarations for TSCH.