diff --git a/arch/platform/cooja/dev/cooja-radio.c b/arch/platform/cooja/dev/cooja-radio.c index 4f7e6a357..f0dee567b 100644 --- a/arch/platform/cooja/dev/cooja-radio.c +++ b/arch/platform/cooja/dev/cooja-radio.c @@ -330,6 +330,10 @@ get_value(radio_param_t param, radio_value_t *value) case RADIO_PARAM_LAST_LINK_QUALITY: *value = simLQI; return RADIO_RESULT_OK; + case RADIO_PARAM_RSSI: + /* return a fixed value depending on the channel */ + *value = -90 + simRadioChannel - 11; + return RADIO_RESULT_OK; default: return RADIO_RESULT_NOT_SUPPORTED; } diff --git a/examples/6tisch/tsch-stats/Makefile b/examples/6tisch/tsch-stats/Makefile new file mode 100644 index 000000000..20592ebad --- /dev/null +++ b/examples/6tisch/tsch-stats/Makefile @@ -0,0 +1,28 @@ +CONTIKI_PROJECT = node +all: $(CONTIKI_PROJECT) + +CONTIKI=../../.. + +# force Orchestra from command line +MAKE_WITH_ORCHESTRA ?= 0 +# force Security from command line +MAKE_WITH_SECURITY ?= 0 +# print #routes periodically, used for regression tests +MAKE_WITH_PERIODIC_ROUTES_PRINT ?= 0 + +MAKE_MAC = MAKE_MAC_TSCH +MODULES += os/services/shell + +ifeq ($(MAKE_WITH_ORCHESTRA),1) +MODULES += os/services/orchestra +endif + +ifeq ($(MAKE_WITH_SECURITY),1) +CFLAGS += -DWITH_SECURITY=1 +endif + +ifeq ($(MAKE_WITH_PERIODIC_ROUTES_PRINT),1) +CFLAGS += -DWITH_PERIODIC_ROUTES_PRINT=1 +endif + +include $(CONTIKI)/Makefile.include diff --git a/examples/6tisch/tsch-stats/README.md b/examples/6tisch/tsch-stats/README.md new file mode 100644 index 000000000..1ee915f9d --- /dev/null +++ b/examples/6tisch/tsch-stats/README.md @@ -0,0 +1 @@ +Demonstration of TSCH stats. \ No newline at end of file diff --git a/examples/6tisch/tsch-stats/node.c b/examples/6tisch/tsch-stats/node.c new file mode 100644 index 000000000..b27821c65 --- /dev/null +++ b/examples/6tisch/tsch-stats/node.c @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2015, SICS Swedish ICT. + * Copyright (c) 2017, University of Bristol - http://www.bristol.ac.uk + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ +/** + * \file + * A RPL+TSCH node. + * + * \author Simon Duquennoy + * Atis Elsts + */ + +#include "contiki.h" +#include "node-id.h" +#include "rpl.h" +#include "rpl-dag-root.h" +#include "sys/log.h" +#include "net/ipv6/uip-ds6-route.h" +#include "net/mac/tsch/tsch.h" +#include "net/mac/tsch/tsch-log.h" +#if UIP_CONF_IPV6_RPL_LITE == 0 +#include "rpl-private.h" +#endif /* UIP_CONF_IPV6_RPL_LITE == 0 */ + +#define DEBUG DEBUG_PRINT +#include "net/ipv6/uip-debug.h" + +/*---------------------------------------------------------------------------*/ +PROCESS(node_process, "RPL Node"); +AUTOSTART_PROCESSES(&node_process); + +/*---------------------------------------------------------------------------*/ +PROCESS_THREAD(node_process, ev, data) +{ + int is_coordinator; + + PROCESS_BEGIN(); + + is_coordinator = (node_id == 1); + + if(is_coordinator) { + rpl_dag_root_init_dag_immediately(); + } + NETSTACK_MAC.on(); + +#if WITH_PERIODIC_ROUTES_PRINT + { + static struct etimer et; + /* Print out routing tables every minute */ + etimer_set(&et, CLOCK_SECOND * 60); + while(1) { + /* Used for non-regression testing */ + #if RPL_WITH_STORING + PRINTF("Routing entries: %u\n", uip_ds6_route_num_routes()); + #endif + #if RPL_WITH_NON_STORING + PRINTF("Routing links: %u\n", rpl_ns_num_nodes()); + #endif + PROCESS_YIELD_UNTIL(etimer_expired(&et)); + etimer_reset(&et); + } + } +#endif /* WITH_PERIODIC_ROUTES_PRINT */ + + PROCESS_END(); +} +/*---------------------------------------------------------------------------*/ diff --git a/examples/6tisch/tsch-stats/project-conf.h b/examples/6tisch/tsch-stats/project-conf.h new file mode 100644 index 000000000..5a2248dae --- /dev/null +++ b/examples/6tisch/tsch-stats/project-conf.h @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2015, SICS Swedish ICT. + * Copyright (c) 2017, University of Bristol - http://www.bristol.ac.uk + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +/** + * \author Simon Duquennoy + * Atis Elsts + */ + +#ifndef __PROJECT_CONF_H__ +#define __PROJECT_CONF_H__ + +/* Set to enable TSCH security */ +#ifndef WITH_SECURITY +#define WITH_SECURITY 0 +#endif /* WITH_SECURITY */ + +/* USB serial takes space, free more space elsewhere */ +#define SICSLOWPAN_CONF_FRAG 0 +#define UIP_CONF_BUFFER_SIZE 160 + +/*******************************************************/ +/******************* Configure TSCH ********************/ +/*******************************************************/ + +/* IEEE802.15.4 PANID */ +#define IEEE802154_CONF_PANID 0x81a5 + +/* Do not start TSCH at init, wait for NETSTACK_MAC.on() */ +#define TSCH_CONF_AUTOSTART 0 + +/* 6TiSCH minimal schedule length. + * Larger values result in less frequent active slots: reduces capacity and saves energy. */ +#define TSCH_SCHEDULE_CONF_DEFAULT_LENGTH 3 + +#if WITH_SECURITY + +/* Enable security */ +#define LLSEC802154_CONF_ENABLED 1 + +#endif /* WITH_SECURITY */ + +/* Enable TSCH statistics */ +#define TSCH_STATS_CONF_ON 1 + +/* Enable periodic RSSI sampling for TSCH statistics */ +#define TSCH_STATS_CONF_SAMPLE_NOISE_RSSI 1 + +/* Reduce the TSCH stat "decay to normal" period */ +#define TSCH_STATS_CONF_DECAY_INTERVAL (60 * CLOCK_SECOND) + +/*******************************************************/ +/************* Other system configuration **************/ +/*******************************************************/ + +/* Logging */ +#define LOG_CONF_LEVEL_RPL LOG_LEVEL_INFO +#define LOG_CONF_LEVEL_TCPIP LOG_LEVEL_WARN +#define LOG_CONF_LEVEL_IPV6 LOG_LEVEL_WARN +#define LOG_CONF_LEVEL_6LOWPAN LOG_LEVEL_WARN +#define LOG_CONF_LEVEL_MAC LOG_LEVEL_DBG +#define LOG_CONF_LEVEL_FRAMER LOG_LEVEL_INFO +#define TSCH_LOG_CONF_PER_SLOT 1 + +#endif /* __PROJECT_CONF_H__ */ diff --git a/examples/6tisch/tsch-stats/tsch-stats-cooja.csc b/examples/6tisch/tsch-stats/tsch-stats-cooja.csc new file mode 100644 index 000000000..76f87e9be --- /dev/null +++ b/examples/6tisch/tsch-stats/tsch-stats-cooja.csc @@ -0,0 +1,263 @@ + + + [APPS_DIR]/mrm + [APPS_DIR]/mspsim + [APPS_DIR]/avrora + [APPS_DIR]/serial_socket + [APPS_DIR]/collect-view + [APPS_DIR]/powertracker + + TSCH stats + 123456 + 1000000 + + org.contikios.cooja.radiomediums.UDGM + 50.0 + 100.0 + 1.0 + 0.5 + + + 40000 + + + org.contikios.cooja.contikimote.ContikiMoteType + mtype660 + RPL/TSCH Node + [CONFIG_DIR]/node.c + make TARGET=cooja clean + make TARGET=cooja node.cooja + org.contikios.cooja.interfaces.Position + org.contikios.cooja.interfaces.Battery + org.contikios.cooja.contikimote.interfaces.ContikiVib + org.contikios.cooja.contikimote.interfaces.ContikiMoteID + org.contikios.cooja.contikimote.interfaces.ContikiRS232 + org.contikios.cooja.contikimote.interfaces.ContikiBeeper + org.contikios.cooja.interfaces.RimeAddress + org.contikios.cooja.contikimote.interfaces.ContikiIPAddress + org.contikios.cooja.contikimote.interfaces.ContikiRadio + org.contikios.cooja.contikimote.interfaces.ContikiButton + org.contikios.cooja.contikimote.interfaces.ContikiPIR + org.contikios.cooja.contikimote.interfaces.ContikiClock + org.contikios.cooja.contikimote.interfaces.ContikiLED + org.contikios.cooja.contikimote.interfaces.ContikiCFS + org.contikios.cooja.interfaces.Mote2MoteRelations + org.contikios.cooja.interfaces.MoteAttributes + false + + + + org.contikios.cooja.interfaces.Position + -1.285769821276336 + 38.58045647334346 + 0.0 + + + org.contikios.cooja.contikimote.interfaces.ContikiMoteID + 1 + + + org.contikios.cooja.contikimote.interfaces.ContikiRadio + 250.0 + + mtype660 + + + + org.contikios.cooja.interfaces.Position + -19.324109516886306 + 76.23135780254927 + 0.0 + + + org.contikios.cooja.contikimote.interfaces.ContikiMoteID + 2 + + + org.contikios.cooja.contikimote.interfaces.ContikiRadio + 250.0 + + mtype660 + + + + org.contikios.cooja.interfaces.Position + 5.815501305791592 + 76.77463755494317 + 0.0 + + + org.contikios.cooja.contikimote.interfaces.ContikiMoteID + 3 + + + org.contikios.cooja.contikimote.interfaces.ContikiRadio + 250.0 + + mtype660 + + + + org.contikios.cooja.interfaces.Position + 31.920697784030082 + 50.5212265977149 + 0.0 + + + org.contikios.cooja.contikimote.interfaces.ContikiMoteID + 4 + + + org.contikios.cooja.contikimote.interfaces.ContikiRadio + 250.0 + + mtype660 + + + + org.contikios.cooja.interfaces.Position + 47.21747673247198 + 30.217765340599726 + 0.0 + + + org.contikios.cooja.contikimote.interfaces.ContikiMoteID + 5 + + + org.contikios.cooja.contikimote.interfaces.ContikiRadio + 250.0 + + mtype660 + + + + org.contikios.cooja.interfaces.Position + 10.622284947035123 + 109.81862399725188 + 0.0 + + + org.contikios.cooja.contikimote.interfaces.ContikiMoteID + 6 + + + org.contikios.cooja.contikimote.interfaces.ContikiRadio + 250.0 + + mtype660 + + + + org.contikios.cooja.interfaces.Position + 52.41150716335335 + 109.93228340481916 + 0.0 + + + org.contikios.cooja.contikimote.interfaces.ContikiMoteID + 7 + + + org.contikios.cooja.contikimote.interfaces.ContikiRadio + 250.0 + + mtype660 + + + + org.contikios.cooja.interfaces.Position + 70.18727461718498 + 70.06861701541145 + 0.0 + + + org.contikios.cooja.contikimote.interfaces.ContikiMoteID + 8 + + + org.contikios.cooja.contikimote.interfaces.ContikiRadio + 250.0 + + mtype660 + + + + org.contikios.cooja.interfaces.Position + 80.29870484201041 + 99.37351603835938 + 0.0 + + + org.contikios.cooja.contikimote.interfaces.ContikiMoteID + 9 + + + org.contikios.cooja.contikimote.interfaces.ContikiRadio + 250.0 + + mtype660 + + + + org.contikios.cooja.plugins.SimControl + 242 + 4 + 160 + 11 + 241 + + + org.contikios.cooja.plugins.Visualizer + + true + org.contikios.cooja.plugins.skins.IDVisualizerSkin + org.contikios.cooja.plugins.skins.GridVisualizerSkin + org.contikios.cooja.plugins.skins.TrafficVisualizerSkin + org.contikios.cooja.plugins.skins.UDGMVisualizerSkin + 1.7405603810040515 0.0 0.0 1.7405603810040515 47.95980153208088 -42.576134155447555 + + 236 + 3 + 230 + 1 + 1 + + + org.contikios.cooja.plugins.LogListener + + + + + + 1031 + 0 + 394 + 273 + 6 + + + org.contikios.cooja.plugins.TimeLine + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + + + + 16529.88882215865 + + 1304 + 2 + 311 + 0 + 412 + + + diff --git a/os/net/mac/tsch/tsch-queue.c b/os/net/mac/tsch/tsch-queue.c index 4ef5130fb..f586ac90e 100644 --- a/os/net/mac/tsch/tsch-queue.c +++ b/os/net/mac/tsch/tsch-queue.c @@ -173,6 +173,8 @@ tsch_queue_update_time_source(const linkaddr_t *new_addr) old_time_src->is_time_source = 0; } + tsch_stats_reset_neighbor_stats(); + #ifdef TSCH_CALLBACK_NEW_TIME_SOURCE TSCH_CALLBACK_NEW_TIME_SOURCE(old_time_src, new_time_src); #endif diff --git a/os/net/mac/tsch/tsch-slot-operation.c b/os/net/mac/tsch/tsch-slot-operation.c index 917d13816..843b79f42 100644 --- a/os/net/mac/tsch/tsch-slot-operation.c +++ b/os/net/mac/tsch/tsch-slot-operation.c @@ -625,6 +625,7 @@ PT_THREAD(tsch_tx_slot(struct pt *pt, struct rtimer *t)) "!truncated dr %d %d", (int)eack_time_correction, (int)drift_correction); ); } + tsch_stats_on_time_synchronization(eack_time_correction); is_drift_correction_used = 1; tsch_timesync_update(current_neighbor, since_last_timesync, drift_correction); /* Keep track of sync time */ @@ -666,6 +667,11 @@ PT_THREAD(tsch_tx_slot(struct pt *pt, struct rtimer *t)) ringbufindex_put(&dequeued_ringbuf); } + /* If this is an unicast packet to timesource, update stats */ + if(current_neighbor != NULL && current_neighbor->is_time_source) { + tsch_stats_tx_packet(current_neighbor, mac_tx_status, tsch_current_channel); + } + /* Log every tx attempt */ TSCH_LOG_ADD(tsch_log_tx, log->tx.mac_tx_status = mac_tx_status; @@ -764,6 +770,7 @@ PT_THREAD(tsch_rx_slot(struct pt *pt, struct rtimer *t)) static int header_len; static frame802154_t frame; radio_value_t radio_last_rssi; + radio_value_t radio_last_lqi; /* Read packet */ current_input->len = NETSTACK_RADIO.read((void *)current_input->payload, TSCH_PACKET_MAX_LEN); @@ -821,6 +828,7 @@ PT_THREAD(tsch_rx_slot(struct pt *pt, struct rtimer *t)) int do_nack = 0; rx_count++; estimated_drift = RTIMER_CLOCK_DIFF(expected_rx_time, rx_start_time); + tsch_stats_on_time_synchronization(estimated_drift); #if TSCH_TIMESYNC_REMOVE_JITTER /* remove jitter due to measurement errors */ @@ -889,6 +897,12 @@ PT_THREAD(tsch_rx_slot(struct pt *pt, struct rtimer *t)) /* Add current input to ringbuf */ ringbufindex_put(&input_ringbuf); + /* If the neighbor is known, update its stats */ + if(n != NULL) { + NETSTACK_RADIO.get_value(RADIO_PARAM_LAST_LINK_QUALITY, &radio_last_lqi); + tsch_stats_rx_packet(n, current_input->rssi, radio_last_lqi, tsch_current_channel); + } + /* Log every reception */ TSCH_LOG_ADD(tsch_log_rx, linkaddr_copy(&log->rx.src, (linkaddr_t *)&frame.src_addr); @@ -951,6 +965,8 @@ PT_THREAD(tsch_slot_operation(struct rtimer *t, void *ptr)) int is_active_slot; TSCH_DEBUG_SLOT_START(); tsch_in_slot_operation = 1; + /* Measure on-air noise level while TSCH is idle */ + tsch_stats_sample_rssi(); /* Reset drift correction */ drift_correction = 0; is_drift_correction_used = 0; diff --git a/os/net/mac/tsch/tsch-stats.c b/os/net/mac/tsch/tsch-stats.c new file mode 100644 index 000000000..474728e6d --- /dev/null +++ b/os/net/mac/tsch/tsch-stats.c @@ -0,0 +1,236 @@ +/* + * Copyright (c) 2016-2017, University of Bristol - http://www.bristol.ac.uk + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +/** + * \file + * Source file for TSCH statistics + * \author + * Atis Elsts + */ + +#include "contiki.h" +#include "net/mac/tsch/tsch.h" +#include "net/netstack.h" +#include "dev/radio.h" + +/* Log configuration */ +#include "sys/log.h" +#define LOG_MODULE "TSCH Stats" +#define LOG_LEVEL LOG_LEVEL_MAC + +/*---------------------------------------------------------------------------*/ +#if TSCH_STATS_ON +/*---------------------------------------------------------------------------*/ + +struct tsch_global_stats tsch_stats; +struct tsch_neighbor_stats tsch_neighbor_stats; + +/* Called every TSCH_STATS_DECAY_INTERVAL ticks */ +static struct ctimer periodic_timer; + +static void periodic(void *); + +/*---------------------------------------------------------------------------*/ +void +tsch_stats_init(void) +{ +#if TSCH_STATS_SAMPLE_NOISE_RSSI + int i; + + for(i = 0; i < TSCH_STATS_NUM_CHANNELS; ++i) { + tsch_stats.noise_rssi[i] = TSCH_STATS_DEFAULT_RSSI; + tsch_stats.channel_free_ewma[i] = TSCH_STATS_DEFAULT_CHANNEL_FREE; + } +#endif + + tsch_stats_reset_neighbor_stats(); + + /* Start the periodic processing soonish */ + ctimer_set(&periodic_timer, TSCH_STATS_DECAY_INTERVAL / 10, periodic, NULL); +} +/*---------------------------------------------------------------------------*/ +void +tsch_stats_reset_neighbor_stats(void) +{ + int i; + struct tsch_channel_stats *ch_stats; + + ch_stats = tsch_neighbor_stats.channel_stats; + for(i = 0; i < TSCH_STATS_NUM_CHANNELS; ++i) { + ch_stats[i].rssi = TSCH_STATS_DEFAULT_RSSI; + ch_stats[i].lqi = TSCH_STATS_DEFAULT_LQI; + ch_stats[i].p_tx_success = TSCH_STATS_DEFAULT_P_TX; + } +} +/*---------------------------------------------------------------------------*/ +struct tsch_neighbor_stats * +tsch_stats_get_from_neighbor(struct tsch_neighbor *n) +{ + /* Due to RAM limitations, this module only collects neighbor stats about the time source */ + if(n != NULL && n->is_time_source) { + return &tsch_neighbor_stats; + } + return NULL; +} +/*---------------------------------------------------------------------------*/ +void +tsch_stats_tx_packet(struct tsch_neighbor *n, uint8_t mac_status, uint8_t channel) +{ + struct tsch_neighbor_stats *stats; + + stats = tsch_stats_get_from_neighbor(n); + if(stats != NULL) { + uint8_t index = tsch_stats_channel_to_index(channel); + uint16_t new_tx_value = (mac_status == MAC_TX_OK ? 1 : 0); + new_tx_value *= TSCH_STATS_BINARY_SCALING_FACTOR; + TSCH_STATS_EWMA_UPDATE(stats->channel_stats[index].p_tx_success, new_tx_value); + } +} +/*---------------------------------------------------------------------------*/ +void +tsch_stats_rx_packet(struct tsch_neighbor *n, int8_t rssi, uint8_t lqi, uint8_t channel) +{ + struct tsch_neighbor_stats *stats; + + stats = tsch_stats_get_from_neighbor(n); + if(stats != NULL) { + uint8_t index = tsch_stats_channel_to_index(channel); + + TSCH_STATS_EWMA_UPDATE(stats->channel_stats[index].rssi, + TSCH_STATS_TRANSFORM(rssi, TSCH_STATS_RSSI_SCALING_FACTOR)); + TSCH_STATS_EWMA_UPDATE(stats->channel_stats[index].lqi, + TSCH_STATS_TRANSFORM(lqi, TSCH_STATS_LQI_SCALING_FACTOR)); + } +} +/*---------------------------------------------------------------------------*/ +void +tsch_stats_on_time_synchronization(int32_t sync_error) +{ + /* Update the maximal error so far if the absolute value of the new one is larger */ + tsch_stats.max_sync_error = MAX(tsch_stats.max_sync_error, ABS(sync_error)); +} +/*---------------------------------------------------------------------------*/ +void +tsch_stats_sample_rssi(void) +{ +#if TSCH_STATS_SAMPLE_NOISE_RSSI + uint8_t index; + radio_value_t value; + radio_result_t rv; + + static uint8_t measurement_channel = TSCH_STATS_FIRST_CHANNEL; + + index = tsch_stats_channel_to_index(measurement_channel); + + /* Select the measurement channel */ + NETSTACK_RADIO.set_value(RADIO_PARAM_CHANNEL, measurement_channel); + + /* Need to explicitly turn on for Coojamotes */ + NETSTACK_RADIO.on(); + + /* Measure the background noise RSSI and act on it */ + rv = NETSTACK_RADIO.get_value(RADIO_PARAM_RSSI, &value); + if(rv == RADIO_RESULT_OK) { + tsch_stat_t prev_busyness_metric; + uint16_t is_free; + + is_free = (((int)value <= TSCH_STATS_BUSY_CHANNEL_RSSI) ? 1 : 0) * TSCH_STATS_BINARY_SCALING_FACTOR; + + /* LOG_DBG("noise RSSI on %u: %d\n", measurement_channel, (int)value); */ + + TSCH_STATS_EWMA_UPDATE(tsch_stats.noise_rssi[index], + TSCH_STATS_TRANSFORM((int)value, TSCH_STATS_RSSI_SCALING_FACTOR)); + + prev_busyness_metric = tsch_stats.channel_free_ewma[index]; + (void)prev_busyness_metric; + TSCH_STATS_EWMA_UPDATE(tsch_stats.channel_free_ewma[index], is_free); + + /* potentially select a new TSCH hopping sequence */ +#ifdef TSCH_CALLBACK_CHANNEL_STATS_UPDATED + TSCH_CALLBACK_CHANNEL_STATS_UPDATED(measurement_channel, prev_busyness_metric); +#endif + } else { + LOG_ERR("! sampling RSSI failed: %d\n", (int)rv); + } + + /* Increment the channel index for the next time */ + measurement_channel++; + if(measurement_channel >= TSCH_STATS_FIRST_CHANNEL + TSCH_STATS_NUM_CHANNELS) { + measurement_channel = TSCH_STATS_FIRST_CHANNEL; + } +#endif /* TSCH_STATS_SAMPLE_NOISE_RSSI */ +} +/*---------------------------------------------------------------------------*/ +/* Periodic timer called every TSCH_STATS_DECAY_INTERVAL ticks */ +static void +periodic(void *ptr) +{ + int i; + struct tsch_neighbor *timesource; + struct tsch_channel_stats *stats = tsch_neighbor_stats.channel_stats; + +#if TSCH_STATS_SAMPLE_NOISE_RSSI + LOG_DBG("Noise RSSI:\n"); + for(i = 0; i < TSCH_STATS_NUM_CHANNELS; ++i) { + LOG_DBG(" channel %u: %d rssi, %u/%u free\n", + TSCH_STATS_FIRST_CHANNEL + i, + tsch_stats.noise_rssi[i] / TSCH_STATS_RSSI_SCALING_FACTOR, + tsch_stats.channel_free_ewma[i], + TSCH_STATS_BINARY_SCALING_FACTOR); + } +#endif + + timesource = tsch_queue_get_time_source(); + if(timesource != NULL) { + LOG_DBG("Time source neighbor:\n"); + + for(i = 0; i < TSCH_STATS_NUM_CHANNELS; ++i) { + LOG_DBG(" channel %u: %d rssi, %u lqi, %u/%u P(tx)\n", + TSCH_STATS_FIRST_CHANNEL + i, + stats[i].rssi / TSCH_STATS_RSSI_SCALING_FACTOR, + stats[i].lqi / TSCH_STATS_LQI_SCALING_FACTOR, + stats[i].p_tx_success, + TSCH_STATS_BINARY_SCALING_FACTOR); + } + } + + /* Do not decay the periodic global stats, as they are updated independely of packet rate */ + for(i = 0; i < TSCH_STATS_NUM_CHANNELS; ++i) { + /* decay Rx stats */ + TSCH_STATS_EWMA_UPDATE(stats[i].rssi, TSCH_STATS_DEFAULT_RSSI); + TSCH_STATS_EWMA_UPDATE(stats[i].lqi, TSCH_STATS_DEFAULT_LQI); + /* decay Tx stats */ + TSCH_STATS_EWMA_UPDATE(stats[i].p_tx_success, TSCH_STATS_DEFAULT_P_TX); + } + + ctimer_set(&periodic_timer, TSCH_STATS_DECAY_INTERVAL, periodic, NULL); +} +/*---------------------------------------------------------------------------*/ +#endif /* TSCH_STATS_ON */ +/*---------------------------------------------------------------------------*/ diff --git a/os/net/mac/tsch/tsch-stats.h b/os/net/mac/tsch/tsch-stats.h new file mode 100644 index 000000000..794cf1df1 --- /dev/null +++ b/os/net/mac/tsch/tsch-stats.h @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2016-2017, University of Bristol - http://www.bristol.ac.uk + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +/** + * \file + * Header file for TSCH statistics + * \author + * Atis Elsts + */ + +/** + * \addtogroup tsch + * @{ +*/ + +#ifndef __TSCH_STATS_H__ +#define __TSCH_STATS_H__ + +/********** Includes **********/ + +#include "contiki.h" +#include "net/linkaddr.h" +#include "net/mac/tsch/tsch-conf.h" +#include "net/mac/tsch/tsch-queue.h" + +/************ Constants ***********/ + +/* Enable the collection of TSCH statistics? */ +#ifdef TSCH_STATS_CONF_ON +#define TSCH_STATS_ON TSCH_STATS_CONF_ON +#else +#define TSCH_STATS_ON 0 +#endif + +/* Enable the collection background noise RSSI? */ +#ifdef TSCH_STATS_CONF_SAMPLE_NOISE_RSSI +#define TSCH_STATS_SAMPLE_NOISE_RSSI TSCH_STATS_CONF_SAMPLE_NOISE_RSSI +#else +#define TSCH_STATS_SAMPLE_NOISE_RSSI 0 +#endif + +/* + * How to update a TSCH statistic. + * Uses a hardcoded EWMA alpha value equal to 0.125 by default. + */ +#ifdef TSCH_STATS_CONF_EWMA_UPDATE +#define TSCH_STATS_EWMA_UPDATE TSCH_STATS_CONF_EWMA_UPDATE +#else +#define TSCH_STATS_EWMA_UPDATE(x, v) (x) = (((x) * 7 / 8) + (v) / 8) +#endif + +/* + * A channel is considered busy if at the sampling instant + * it has RSSI higher or equal to this limit. + */ +#ifdef TSCH_STATS_CONF_BUSY_CHANNEL_RSSI +#define TSCH_STATS_BUSY_CHANNEL_RSSI TSCH_STATS_CONF_BUSY_CHANNEL_RSSI +#else +#define TSCH_STATS_BUSY_CHANNEL_RSSI -85 +#endif + +/* The period after which stat values are decayed towards the default values */ +#ifdef TSCH_STATS_CONF_DECAY_INTERVAL +#define TSCH_STATS_DECAY_INTERVAL TSCH_STATS_CONF_DECAY_INTERVAL +#else +#define TSCH_STATS_DECAY_INTERVAL (20ul * 60 * CLOCK_SECOND) +#endif + +/* + * The total number of MAC-layer channels. + * Sixteen for the IEEE802.15.4 2.4 GHz band. + */ +#ifdef TSCH_STATS_CONF_NUM_CHANNELS +#define TSCH_STATS_NUM_CHANNELS TSCH_STATS_CONF_NUM_CHANNELS +#else +#define TSCH_STATS_NUM_CHANNELS 16 +#endif + +/* The number of the first MAC-layer channel. */ +#ifdef TSCH_STATS_CONF_FIRST_CHANNEL +#define TSCH_STATS_FIRST_CHANNEL TSCH_STATS_CONF_FIRST_CHANNEL +#else +#define TSCH_STATS_FIRST_CHANNEL 11 +#endif + +/* Internal: the scaling of the various stats */ +#define TSCH_STATS_RSSI_SCALING_FACTOR -16 +#define TSCH_STATS_LQI_SCALING_FACTOR 16 +#define TSCH_STATS_BINARY_SCALING_FACTOR 4096 + +/* + * Transform a statistic from external form to the internal representation. + * To transform back, simply divide by the factor. + */ +#define TSCH_STATS_TRANSFORM(x, factor) ((int16_t)(x) * factor) + +/* The default value for RSSI statistics: -90 dBm */ +#define TSCH_STATS_DEFAULT_RSSI TSCH_STATS_TRANSFORM(-90, TSCH_STATS_RSSI_SCALING_FACTOR) +/* The default value for LQI statistics: 100 */ +#define TSCH_STATS_DEFAULT_LQI TSCH_STATS_TRANSFORM(100, TSCH_STATS_LQI_SCALING_FACTOR) +/* The default value for P_tx (packet transmission probability) statistics: 50% */ +#define TSCH_STATS_DEFAULT_P_TX (TSCH_STATS_BINARY_SCALING_FACTOR / 2) +/* The default value for channel free status: 100% */ +#define TSCH_STATS_DEFAULT_CHANNEL_FREE TSCH_STATS_BINARY_SCALING_FACTOR + +/* #define these callbacks to do the adaptive channel selection based on RSSI */ +/* TSCH_CALLBACK_CHANNEL_STATS_UPDATED(channel, previous_metric); */ +/* TSCH_CALLBACK_SELECT_CHANNELS(); */ + + +/************ Types ***********/ + +typedef uint16_t tsch_stat_t; + +struct tsch_global_stats { + /* the maximum synchronization error */ + uint32_t max_sync_error; + /* number of disassociations */ + uint16_t num_disassociations; +#if TSCH_STATS_SAMPLE_NOISE_RSSI + /* per-channel noise estimates */ + tsch_stat_t noise_rssi[TSCH_STATS_NUM_CHANNELS]; + /* derived from `noise_rssi` and BUSY_CHANNEL_RSSI */ + tsch_stat_t channel_free_ewma[TSCH_STATS_NUM_CHANNELS]; +#endif /* TSCH_STATS_SAMPLE_NOISE_RSSI */ +}; + +struct tsch_channel_stats { + /* EWMA, from receptions */ + tsch_stat_t rssi; + /* EWMA, from receptions */ + tsch_stat_t lqi; + /* EWMA of probability, for unicast transmissions only */ + tsch_stat_t p_tx_success; +}; + +struct tsch_neighbor_stats { + struct tsch_channel_stats channel_stats[TSCH_STATS_NUM_CHANNELS]; +}; + +struct tsch_neighbor; /* Forward declaration */ + + +/************ External variables ***********/ + +#if TSCH_STATS_ON + +/* Statistics for the local node */ +extern struct tsch_global_stats tsch_stats; + +/* For the timesource neighbor */ +extern struct tsch_neighbor_stats tsch_neighbor_stats; + + +/************ Functions ***********/ + +void tsch_stats_init(void); + +void tsch_stats_tx_packet(struct tsch_neighbor *, uint8_t mac_status, uint8_t channel); + +void tsch_stats_rx_packet(struct tsch_neighbor *, int8_t rssi, uint8_t lqi, uint8_t channel); + +void tsch_stats_on_time_synchronization(int32_t sync_error); + +void tsch_stats_sample_rssi(void); + +struct tsch_neighbor_stats *tsch_stats_get_from_neighbor(struct tsch_neighbor *); + +void tsch_stats_reset_neighbor_stats(void); + +#else /* TSCH_STATS_ON */ + +#define tsch_stats_init() +#define tsch_stats_tx_packet(n, mac_status, channel) +#define tsch_stats_rx_packet(n, rssi, lqi, channel) +#define tsch_stats_on_time_synchronization(sync_error) +#define tsch_stats_sample_rssi() +#define tsch_stats_get_from_neighbor(neighbor) NULL +#define tsch_stats_reset_neighbor_stats() + +#endif /* TSCH_STATS_ON */ + +static inline uint8_t +tsch_stats_channel_to_index(uint8_t channel) +{ + return channel - TSCH_STATS_FIRST_CHANNEL; +} + +static inline uint8_t +tsch_stats_index_to_channel(uint8_t channel_index) +{ + return channel_index + TSCH_STATS_FIRST_CHANNEL; +} + + +#endif /* __TSCH_STATS_H__ */ +/** @} */ diff --git a/os/net/mac/tsch/tsch.c b/os/net/mac/tsch/tsch.c index 45df20e6f..e67e11c7c 100644 --- a/os/net/mac/tsch/tsch.c +++ b/os/net/mac/tsch/tsch.c @@ -864,6 +864,9 @@ PROCESS_THREAD(tsch_pending_events_process, ev, data) tsch_rx_process_pending(); tsch_tx_process_pending(); tsch_log_process_pending(); +#ifdef TSCH_CALLBACK_SELECT_CHANNELS + TSCH_CALLBACK_SELECT_CHANNELS(); +#endif } PROCESS_END(); } @@ -944,6 +947,8 @@ tsch_init(void) #if TSCH_WITH_SIXTOP sixtop_init(); #endif + + tsch_stats_init(); } /*---------------------------------------------------------------------------*/ /* Function send for TSCH-MAC, puts the packet in packetbuf in the MAC queue */ diff --git a/os/net/mac/tsch/tsch.h b/os/net/mac/tsch/tsch.h index 88ebb4db6..273f979e4 100644 --- a/os/net/mac/tsch/tsch.h +++ b/os/net/mac/tsch/tsch.h @@ -60,6 +60,7 @@ frequency hopping for enhanced reliability. #include "net/mac/tsch/tsch-packet.h" #include "net/mac/tsch/tsch-security.h" #include "net/mac/tsch/tsch-schedule.h" +#include "net/mac/tsch/tsch-stats.h" #if UIP_CONF_IPV6_RPL #include "net/mac/tsch/tsch-rpl.h" #endif /* UIP_CONF_IPV6_RPL */