Merge pull request #637 from atiselsts/contrib/tsch-channel-selection

TSCH stats and adaptive TSCH channel selection
This commit is contained in:
Simon Duquennoy 2018-09-28 14:15:26 +02:00 committed by GitHub
commit f91ef005bc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 1376 additions and 0 deletions

View File

@ -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;
}

View File

@ -0,0 +1,34 @@
CONTIKI_PROJECT = node
all: $(CONTIKI_PROJECT)
CONTIKI=../../..
PLATFORMS_EXCLUDE = sky nrf52dk native
BOARDS_EXCLUDE = srf06/cc13xx launchpad/cc1310 launchpad/cc1350 sensortag/cc2650 sensortag/cc1350
# The channel selection library
MODULES += os/services/tsch-cs
# 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

View File

@ -0,0 +1,8 @@
Demonstration of TSCH adaptive channel selection based on background noise RSSI metric.
TSCH stats must be enabled for the adaptive selection functionality to compile and work.
This code relies on the `os/services/channel-selection` library that implements
the "RSSI upstream" adaptative channel selection strategy, described in the following paper:
A. Elsts, X. Fafoutis, G. Oikonomou and R. Piechocki. Adaptive Channel Selection in IEEE 802.15.4 TSCH Networks, 1st Global Internet of Things Summit, 2017.
http://ieeexplore.ieee.org/document/8016246/

View File

@ -0,0 +1,94 @@
/*
* Copyright (c) 2015, SICS Swedish ICT.
* Copyright (c) 2018, 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 <simonduq@sics.se>
* Atis Elsts <atis.elsts@bristol.ac.uk>
*/
#include "contiki.h"
#include "sys/node-id.h"
#include "sys/log.h"
#include "net/ipv6/uip-ds6-route.h"
#include "net/ipv6/uip-sr.h"
#include "net/mac/tsch/tsch.h"
#include "net/routing/routing.h"
#include "tsch-cs.h"
#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 = 0;
#if CONTIKI_TARGET_COOJA
is_coordinator = (node_id == 1);
#endif
if(is_coordinator) {
NETSTACK_ROUTING.root_start();
}
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 (UIP_MAX_ROUTES != 0)
PRINTF("Routing entries: %u\n", uip_ds6_route_num_routes());
#endif
#if (UIP_SR_LINK_NUM != 0)
PRINTF("Routing links: %u\n", uip_sr_num_nodes());
#endif
PROCESS_YIELD_UNTIL(etimer_expired(&et));
etimer_reset(&et);
}
}
#endif /* WITH_PERIODIC_ROUTES_PRINT */
PROCESS_END();
}
/*---------------------------------------------------------------------------*/

View File

@ -0,0 +1,109 @@
/*
* 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 <simonduq@sics.se>
* Atis Elsts <atis.elsts@bristol.ac.uk>
*/
#ifndef __PROJECT_CONF_H__
#define __PROJECT_CONF_H__
#include <stdint.h>
#include <stdbool.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: must be on for channel selection to work */
#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 to get printouts more often */
#define TSCH_STATS_CONF_DECAY_INTERVAL (60 * CLOCK_SECOND)
/* For adaptive channel selection */
extern void tsch_cs_channel_stats_updated(uint8_t updated_channel, uint16_t old_busyness_metric);
extern bool tsch_cs_process(void);
/* These will be called from the core TSCH code */
#define TSCH_CALLBACK_CHANNEL_STATS_UPDATED tsch_cs_channel_stats_updated
#define TSCH_CALLBACK_SELECT_CHANNELS tsch_cs_process
/* The coordinator will update the network nodes with new hopping sequences */
#define TSCH_PACKET_CONF_EB_WITH_HOPPING_SEQUENCE 1
/* Reduce the EB period in order to update the network nodes with more agility */
#define TSCH_CONF_EB_PERIOD (4 * CLOCK_SECOND)
#define TSCH_CONF_MAX_EB_PERIOD (4 * 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__ */

View File

@ -0,0 +1,31 @@
CONTIKI_PROJECT = node
all: $(CONTIKI_PROJECT)
CONTIKI=../../..
PLATFORMS_EXCLUDE = sky nrf52dk native
BOARDS_EXCLUDE = srf06/cc13xx launchpad/cc1310 launchpad/cc1350 sensortag/cc2650 sensortag/cc1350
# 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

View File

@ -0,0 +1 @@
Demonstration of TSCH stats.

View File

@ -0,0 +1,93 @@
/*
* Copyright (c) 2015, SICS Swedish ICT.
* Copyright (c) 2018, 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 <simonduq@sics.se>
* Atis Elsts <atis.elsts@bristol.ac.uk>
*/
#include "contiki.h"
#include "sys/node-id.h"
#include "sys/log.h"
#include "net/ipv6/uip-ds6-route.h"
#include "net/ipv6/uip-sr.h"
#include "net/mac/tsch/tsch.h"
#include "net/routing/routing.h"
#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 = 0;
#if CONTIKI_TARGET_COOJA
is_coordinator = (node_id == 1);
#endif
if(is_coordinator) {
NETSTACK_ROUTING.root_start();
}
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 (UIP_MAX_ROUTES != 0)
PRINTF("Routing entries: %u\n", uip_ds6_route_num_routes());
#endif
#if (UIP_SR_LINK_NUM != 0)
PRINTF("Routing links: %u\n", uip_sr_num_nodes());
#endif
PROCESS_YIELD_UNTIL(etimer_expired(&et));
etimer_reset(&et);
}
}
#endif /* WITH_PERIODIC_ROUTES_PRINT */
PROCESS_END();
}
/*---------------------------------------------------------------------------*/

View File

@ -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 <simonduq@sics.se>
* Atis Elsts <atis.elsts@bristol.ac.uk>
*/
#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 to get printouts more often */
#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__ */

View File

@ -53,6 +53,7 @@
#include "services/orchestra/orchestra.h"
#include "services/shell/serial-shell.h"
#include "services/simple-energest/simple-energest.h"
#include "services/tsch-cs/tsch-cs.h"
#include <stdio.h>
#include <stdint.h>
@ -146,6 +147,11 @@ main(void)
simple_energest_init();
#endif /* BUILD_WITH_SIMPLE_ENERGEST */
#if BUILD_WITH_TSCH_CS
/* Initialize the channel selection module */
tsch_cs_adaptations_init();
#endif /* BUILD_WITH_TSCH_CS */
autostart_start(autostart_processes);
watchdog_start();

View File

@ -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

View File

@ -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;

View File

@ -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 <atis.elsts@bristol.ac.uk>
*/
#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 */
/*---------------------------------------------------------------------------*/

View File

@ -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 <atis.elsts@bristol.ac.uk>
*/
/**
* \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__ */
/** @} */

View File

@ -412,6 +412,22 @@ eb_input(struct input_packet *current_input)
}
#endif /* TSCH_AUTOSELECT_TIME_SOURCE */
}
/* TSCH hopping sequence */
if(eb_ies.ie_channel_hopping_sequence_id != 0) {
if(eb_ies.ie_hopping_sequence_len != tsch_hopping_sequence_length.val
|| memcmp((uint8_t *)tsch_hopping_sequence, eb_ies.ie_hopping_sequence_list, tsch_hopping_sequence_length.val)) {
if(eb_ies.ie_hopping_sequence_len <= sizeof(tsch_hopping_sequence)) {
memcpy((uint8_t *)tsch_hopping_sequence, eb_ies.ie_hopping_sequence_list,
eb_ies.ie_hopping_sequence_len);
TSCH_ASN_DIVISOR_INIT(tsch_hopping_sequence_length, eb_ies.ie_hopping_sequence_len);
LOG_WARN("Updating TSCH hopping sequence from EB\n");
} else {
LOG_WARN("TSCH:! parse_eb: hopping sequence too long (%u)\n", eb_ies.ie_hopping_sequence_len);
}
}
}
}
}
}
@ -864,6 +880,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 +963,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 */

View File

@ -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 */

View File

@ -0,0 +1 @@
CFLAGS += -DBUILD_WITH_TSCH_CS=1

View File

@ -0,0 +1,328 @@
/*
* Copyright (c) 2016-2018, 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 adaptive channel selection
* \author
* Atis Elsts <atis.elsts@bristol.ac.uk>
*/
#include "tsch.h"
#include "tsch-stats.h"
#include "tsch-cs.h"
/* Log configuration */
#include "sys/log.h"
#define LOG_MODULE "TSCH CS"
#define LOG_LEVEL LOG_LEVEL_MAC
/*---------------------------------------------------------------------------*/
/* Allow to change only 1 channel at once */
#define TSCH_CS_MAX_CHANNELS_CHANGED 1
/* Do not up change channels more frequently than this */
#define TSCH_CS_MIN_UPDATE_INTERVAL_SEC 60
/* Do not change channels if the difference in qualities is below this */
#define TSCH_CS_HYSTERESIS (TSCH_STATS_BINARY_SCALING_FACTOR / 10)
/* After removing a channel from the sequence, do not add it back at least this time */
#define TSCH_CS_BLACKLIST_DURATION_SEC (5 * 60)
/* A potential for change detected? */
static bool recaculation_requested;
/* Time (in seconds) when channels were marked as busy; 0 if they are not busy */
static uint32_t tsch_cs_busy_since[TSCH_STATS_NUM_CHANNELS];
/*
* The following variables are kept in order to avoid completely migrating away
* from the initial hopping sequence (as then new nodes would not be able to join).
* The invariant is: tsch_cs_initial_bitmap & tsch_cs_current_bitmap != 0
*/
/* The bitmap with the initial channels */
static tsch_cs_bitmap_t tsch_cs_initial_bitmap;
/* The bitmap with the current channels */
static tsch_cs_bitmap_t tsch_cs_current_bitmap;
/* structure for sorting */
struct tsch_cs_quality {
/* channel number */
uint8_t channel;
/* the higher, the better */
tsch_stat_t metric;
};
/*---------------------------------------------------------------------------*/
static inline bool
tsch_cs_bitmap_contains(tsch_cs_bitmap_t bitmap, uint8_t channel)
{
return (1 << (channel - TSCH_STATS_FIRST_CHANNEL)) & bitmap;
}
/*---------------------------------------------------------------------------*/
static inline tsch_cs_bitmap_t
tsch_cs_bitmap_set(tsch_cs_bitmap_t bitmap, uint8_t channel)
{
return (1 << (channel - TSCH_STATS_FIRST_CHANNEL)) | bitmap;
}
/*---------------------------------------------------------------------------*/
static tsch_cs_bitmap_t
tsch_cs_bitmap_calc(void)
{
tsch_cs_bitmap_t result = 0;
int i;
for(i = 0; i < tsch_hopping_sequence_length.val; ++i) {
result = tsch_cs_bitmap_set(result, tsch_hopping_sequence[i]);
}
return result;
}
/*---------------------------------------------------------------------------*/
void
tsch_cs_adaptations_init(void)
{
tsch_cs_initial_bitmap = tsch_cs_bitmap_calc();
tsch_cs_current_bitmap = tsch_cs_initial_bitmap;
}
/*---------------------------------------------------------------------------*/
/* Sort the elements to that the channels with the best metrics are in the front */
static void
tsch_cs_bubble_sort(struct tsch_cs_quality *qualities)
{
int i, j;
struct tsch_cs_quality tmp;
for(i = 0; i < TSCH_STATS_NUM_CHANNELS; ++i) {
for(j = 0; j + 1 < TSCH_STATS_NUM_CHANNELS; ++j) {
if(qualities[j].metric < qualities[j+1].metric){
tmp = qualities[j];
qualities[j] = qualities[j+1];
qualities[j+1] = tmp;
}
}
}
}
/*---------------------------------------------------------------------------*/
/* Select a single, currently unused, good enough channel. Returns 0xff on failure. */
static uint8_t
tsch_cs_select_replacement(uint8_t old_channel, tsch_stat_t old_ewma,
struct tsch_cs_quality *qualities, uint8_t is_in_sequence[])
{
int i;
uint32_t now = clock_seconds();
tsch_cs_bitmap_t bitmap = tsch_cs_bitmap_set(0, old_channel);
/* Don't want to replace a channel if the improvement is miniscule (< 10%) */
old_ewma += TSCH_CS_HYSTERESIS;
/* iterate up to -1 because we know that at least one of the channels is bad */
for(i = 0; i < TSCH_STATS_NUM_CHANNELS - 1; ++i) {
/* select a replacement candidate */
uint8_t candidate = qualities[i].channel;
if(qualities[i].metric < TSCH_CS_FREE_THRESHOLD) {
/* This channel is not good enough.
* since we know that the other channels in the sorted list are even worse,
* it makes sense to return immediately rather than to continue t
*/
LOG_DBG("ch %u: busy\n", candidate);
return 0xff;
}
if(qualities[i].metric < old_ewma) {
/* not good enough to replace */
LOG_DBG("ch %u: hysteresis check failed\n", candidate);
return 0xff;
}
/* already in the current TSCH hopping sequence? */
if(is_in_sequence[candidate - TSCH_STATS_FIRST_CHANNEL] != 0xff) {
LOG_DBG("ch %u: in seq\n", candidate);
continue;
}
/* ignore this candidate if too recently blacklisted */
if(tsch_cs_busy_since[candidate - TSCH_STATS_FIRST_CHANNEL] != 0
&& tsch_cs_busy_since[candidate - TSCH_STATS_FIRST_CHANNEL] + TSCH_CS_BLACKLIST_DURATION_SEC > now) {
LOG_DBG("ch %u: recent bl\n", candidate);
continue;
}
/* check if removing the old channel would break our hopping sequence invariant */
if(bitmap == (tsch_cs_initial_bitmap & tsch_cs_current_bitmap)) {
/* the channel is the only one that belongs to both */
if(!tsch_cs_bitmap_contains(tsch_cs_initial_bitmap, candidate)) {
/* the candidate is not in the initial sequence; not acceptable */
continue;
}
}
return candidate;
}
return 0xff;
}
/*---------------------------------------------------------------------------*/
bool
tsch_cs_process(void)
{
int i;
bool try_replace;
bool has_replaced;
struct tsch_cs_quality qualities[TSCH_STATS_NUM_CHANNELS];
uint8_t is_channel_busy[TSCH_STATS_NUM_CHANNELS];
uint8_t is_in_sequence[TSCH_STATS_NUM_CHANNELS];
static uint32_t last_time_changed;
if(!recaculation_requested) {
/* nothing to do */
return false;
}
if(last_time_changed != 0 && last_time_changed + TSCH_CS_MIN_UPDATE_INTERVAL_SEC > clock_seconds()) {
/* too soon */
return false;
}
/* reset the flag */
recaculation_requested = false;
for(i = 0; i < TSCH_STATS_NUM_CHANNELS; ++i) {
qualities[i].channel = i + TSCH_STATS_FIRST_CHANNEL;
qualities[i].metric = tsch_stats.channel_free_ewma[i];
}
/* bubble sort the channels */
tsch_cs_bubble_sort(qualities);
/* start with the threshold values */
for(i = 0; i < TSCH_STATS_NUM_CHANNELS; ++i) {
is_channel_busy[i] = (tsch_stats.channel_free_ewma[i] < TSCH_CS_FREE_THRESHOLD);
}
memset(is_in_sequence, 0xff, sizeof(is_in_sequence));
for(i = 0; i < tsch_hopping_sequence_length.val; ++i) {
uint8_t channel = tsch_hopping_sequence[i];
is_in_sequence[channel - TSCH_STATS_FIRST_CHANNEL] = i;
}
/* mark the first N channels as "good" - there is nothing better to select */
for(i = 0; i < tsch_hopping_sequence_length.val; ++i) {
is_channel_busy[qualities[i].channel - TSCH_STATS_FIRST_CHANNEL] = 0;
}
for(i = 0; i < TSCH_STATS_NUM_CHANNELS; ++i) {
uint8_t ci = qualities[i].channel - TSCH_STATS_FIRST_CHANNEL;
(void)ci;
LOG_DBG("ch %u q %u busy %u in seq %u\n",
qualities[i].channel,
qualities[i].metric,
is_channel_busy[ci],
is_in_sequence[ci] == 0xff ? 0 : 1);
}
try_replace = false;
for(i = 0; i < tsch_hopping_sequence_length.val; ++i) {
uint8_t channel = tsch_hopping_sequence[i];
if(is_channel_busy[channel - TSCH_STATS_FIRST_CHANNEL]) {
try_replace = true;
}
}
if(!try_replace) {
LOG_DBG("cs: not replacing\n");
return false;
}
has_replaced = false;
for(i = TSCH_STATS_NUM_CHANNELS - 1; i >= tsch_hopping_sequence_length.val; --i) {
if(is_in_sequence[qualities[i].channel - TSCH_STATS_FIRST_CHANNEL] != 0xff) {
/* found the worst channel; it must be busy */
uint8_t channel = qualities[i].channel;
tsch_stat_t ewma_metric = qualities[i].metric;
uint8_t replacement = tsch_cs_select_replacement(channel, ewma_metric,
qualities, is_in_sequence);
uint8_t position = is_in_sequence[channel - TSCH_STATS_FIRST_CHANNEL];
if(replacement != 0xff) {
printf("\ncs: replacing channel %u %u (%u) with %u\n",
channel, tsch_hopping_sequence[position], position, replacement);
/* mark the old channel as busy */
tsch_cs_busy_since[channel - TSCH_STATS_FIRST_CHANNEL] = clock_seconds();
/* do the actual replacement in the global TSCH HS variable */
tsch_hopping_sequence[position] = replacement;
has_replaced = true;
/* recalculate the hopping sequence bitmap */
tsch_cs_current_bitmap = tsch_cs_bitmap_calc();
}
break; /* replace just one at once */
}
}
if(has_replaced) {
last_time_changed = clock_seconds();
return true;
}
LOG_DBG("cs: no changes\n");
return false;
}
/*---------------------------------------------------------------------------*/
void
tsch_cs_channel_stats_updated(uint8_t updated_channel, uint16_t old_busyness_metric)
{
uint8_t index;
bool old_is_busy;
bool new_is_busy;
/* Enable this only on the coordinator node */
if(!tsch_is_coordinator) {
return;
}
/* Do not try to adapt before enough information has been learned */
if(clock_seconds() < TSCH_CS_LEARNING_PERIOD_SEC) {
return;
}
index = tsch_stats_channel_to_index(updated_channel);
old_is_busy = (old_busyness_metric < TSCH_CS_FREE_THRESHOLD);
new_is_busy = (tsch_stats.channel_free_ewma[index] < TSCH_CS_FREE_THRESHOLD);
if(old_is_busy != new_is_busy) {
/* the status of the channel has changed*/
recaculation_requested = true;
} else if(new_is_busy) {
/* run the reselection algorithm iff the channel is both (1) bad and (2) in use */
if(tsch_cs_bitmap_contains(tsch_cs_current_bitmap, updated_channel)) {
/* the channel is in use and is busy */
recaculation_requested = true;
}
}
}

View File

@ -0,0 +1,76 @@
/*
* Copyright (c) 2016-2018, 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 adaptive channel selection
* \author
* Atis Elsts <atis.elsts@bristol.ac.uk>
*/
#ifndef __TSCH_CS_H__
#define __TSCH_CS_H__
#include "contiki.h"
#include <stdbool.h>
/* If `channel_free_ewma` value is less than this, the channel is considered busy */
#ifdef TSCH_CS_CONF_FREE_THRESHOLD
#define TSCH_CS_FREE_THRESHOLD TSCH_CS_CONF_FREE_THRESHOLD
#else
/* < 85% free */
#define TSCH_CS_FREE_THRESHOLD ((tsch_stat_t)(85ul * TSCH_STATS_BINARY_SCALING_FACTOR / 100))
#endif
#define TSCH_CS_LEARNING_PERIOD_SEC 30
/**
* \brief Initializes the TSCH hopping sequence selection module.
*/
void tsch_cs_adaptations_init(void);
/**
* \brief Signal the need to potentially update the TSCH hopping sequence.
* \param updated_channel The channel with the updated RSSI measurement
* \param old_busyness_metric The EWMA value of the "channel busy" status before the last RSSI measurement
*/
void tsch_cs_channel_stats_updated(uint8_t updated_channel, uint16_t old_busyness_metric);
/**
* \brief Potentially update the TSCH hopping sequence
* \return true if the hopping sequence was updated, false otherwise
*/
bool tsch_cs_process(void);
/* A bit corresponds to a channel; `uint16_t` value is OK for up to 16 channels. */
typedef uint16_t tsch_cs_bitmap_t;
#endif /* __TSCH_CS_H__ */