add TSCH channel selection service
This commit is contained in:
parent
ad577cf77f
commit
bc9967dbb0
@ -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();
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
1
os/services/tsch-cs/Makefile
Normal file
1
os/services/tsch-cs/Makefile
Normal file
@ -0,0 +1 @@
|
||||
CFLAGS += -DBUILD_WITH_TSCH_CS=1
|
328
os/services/tsch-cs/tsch-cs.c
Normal file
328
os/services/tsch-cs/tsch-cs.c
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
76
os/services/tsch-cs/tsch-cs.h
Normal file
76
os/services/tsch-cs/tsch-cs.h
Normal 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__ */
|
Loading…
Reference in New Issue
Block a user