43 #define LOG_MODULE "TSCH CS" 44 #define LOG_LEVEL LOG_LEVEL_MAC 49 #define TSCH_CS_MAX_CHANNELS_CHANGED 1 52 #define TSCH_CS_MIN_UPDATE_INTERVAL_SEC 60 55 #define TSCH_CS_HYSTERESIS (TSCH_STATS_BINARY_SCALING_FACTOR / 10) 58 #define TSCH_CS_BLACKLIST_DURATION_SEC (5 * 60) 61 static bool recaculation_requested;
64 static uint32_t tsch_cs_busy_since[TSCH_STATS_NUM_CHANNELS];
72 static tsch_cs_bitmap_t tsch_cs_initial_bitmap;
74 static tsch_cs_bitmap_t tsch_cs_current_bitmap;
77 struct tsch_cs_quality {
85 tsch_cs_bitmap_contains(tsch_cs_bitmap_t bitmap, uint8_t channel)
87 return (1 << (channel - TSCH_STATS_FIRST_CHANNEL)) & bitmap;
90 static inline tsch_cs_bitmap_t
91 tsch_cs_bitmap_set(tsch_cs_bitmap_t bitmap, uint8_t channel)
93 return (1 << (channel - TSCH_STATS_FIRST_CHANNEL)) | bitmap;
96 static tsch_cs_bitmap_t
97 tsch_cs_bitmap_calc(
void)
99 tsch_cs_bitmap_t result = 0;
101 for(i = 0; i < tsch_hopping_sequence_length.val; ++i) {
102 result = tsch_cs_bitmap_set(result, tsch_hopping_sequence[i]);
110 tsch_cs_initial_bitmap = tsch_cs_bitmap_calc();
111 tsch_cs_current_bitmap = tsch_cs_initial_bitmap;
116 tsch_cs_bubble_sort(
struct tsch_cs_quality *qualities)
119 struct tsch_cs_quality tmp;
121 for(i = 0; i < TSCH_STATS_NUM_CHANNELS; ++i) {
122 for(j = 0; j + 1 < TSCH_STATS_NUM_CHANNELS; ++j) {
123 if(qualities[j].metric < qualities[j+1].metric){
125 qualities[j] = qualities[j+1];
126 qualities[j+1] = tmp;
134 tsch_cs_select_replacement(uint8_t old_channel, tsch_stat_t old_ewma,
135 struct tsch_cs_quality *qualities, uint8_t is_in_sequence[])
139 tsch_cs_bitmap_t bitmap = tsch_cs_bitmap_set(0, old_channel);
142 old_ewma += TSCH_CS_HYSTERESIS;
145 for(i = 0; i < TSCH_STATS_NUM_CHANNELS - 1; ++i) {
147 uint8_t candidate = qualities[i].channel;
149 if(qualities[i].metric < TSCH_CS_FREE_THRESHOLD) {
154 LOG_DBG(
"ch %u: busy\n", candidate);
158 if(qualities[i].metric < old_ewma) {
160 LOG_DBG(
"ch %u: hysteresis check failed\n", candidate);
165 if(is_in_sequence[candidate - TSCH_STATS_FIRST_CHANNEL] != 0xff) {
166 LOG_DBG(
"ch %u: in seq\n", candidate);
171 if(tsch_cs_busy_since[candidate - TSCH_STATS_FIRST_CHANNEL] != 0
172 && tsch_cs_busy_since[candidate - TSCH_STATS_FIRST_CHANNEL] + TSCH_CS_BLACKLIST_DURATION_SEC > now) {
173 LOG_DBG(
"ch %u: recent bl\n", candidate);
178 if(bitmap == (tsch_cs_initial_bitmap & tsch_cs_current_bitmap)) {
180 if(!tsch_cs_bitmap_contains(tsch_cs_initial_bitmap, candidate)) {
198 struct tsch_cs_quality qualities[TSCH_STATS_NUM_CHANNELS];
199 uint8_t is_channel_busy[TSCH_STATS_NUM_CHANNELS];
200 uint8_t is_in_sequence[TSCH_STATS_NUM_CHANNELS];
201 static uint32_t last_time_changed;
203 if(!recaculation_requested) {
208 if(last_time_changed != 0 && last_time_changed + TSCH_CS_MIN_UPDATE_INTERVAL_SEC >
clock_seconds()) {
214 recaculation_requested =
false;
216 for(i = 0; i < TSCH_STATS_NUM_CHANNELS; ++i) {
217 qualities[i].channel = i + TSCH_STATS_FIRST_CHANNEL;
218 qualities[i].metric = tsch_stats.channel_free_ewma[i];
222 tsch_cs_bubble_sort(qualities);
225 for(i = 0; i < TSCH_STATS_NUM_CHANNELS; ++i) {
226 is_channel_busy[i] = (tsch_stats.channel_free_ewma[i] < TSCH_CS_FREE_THRESHOLD);
228 memset(is_in_sequence, 0xff,
sizeof(is_in_sequence));
229 for(i = 0; i < tsch_hopping_sequence_length.val; ++i) {
230 uint8_t channel = tsch_hopping_sequence[i];
231 is_in_sequence[channel - TSCH_STATS_FIRST_CHANNEL] = i;
235 for(i = 0; i < tsch_hopping_sequence_length.val; ++i) {
236 is_channel_busy[qualities[i].channel - TSCH_STATS_FIRST_CHANNEL] = 0;
239 for(i = 0; i < TSCH_STATS_NUM_CHANNELS; ++i) {
240 uint8_t ci = qualities[i].channel - TSCH_STATS_FIRST_CHANNEL;
242 LOG_DBG(
"ch %u q %u busy %u in seq %u\n",
243 qualities[i].channel,
246 is_in_sequence[ci] == 0xff ? 0 : 1);
250 for(i = 0; i < tsch_hopping_sequence_length.val; ++i) {
251 uint8_t channel = tsch_hopping_sequence[i];
252 if(is_channel_busy[channel - TSCH_STATS_FIRST_CHANNEL]) {
257 LOG_DBG(
"cs: not replacing\n");
261 has_replaced =
false;
262 for(i = TSCH_STATS_NUM_CHANNELS - 1; i >= tsch_hopping_sequence_length.val; --i) {
263 if(is_in_sequence[qualities[i].channel - TSCH_STATS_FIRST_CHANNEL] != 0xff) {
265 uint8_t channel = qualities[i].channel;
266 tsch_stat_t ewma_metric = qualities[i].metric;
267 uint8_t replacement = tsch_cs_select_replacement(channel, ewma_metric,
268 qualities, is_in_sequence);
269 uint8_t position = is_in_sequence[channel - TSCH_STATS_FIRST_CHANNEL];
271 if(replacement != 0xff) {
272 printf(
"\ncs: replacing channel %u %u (%u) with %u\n",
273 channel, tsch_hopping_sequence[position], position, replacement);
275 tsch_cs_busy_since[channel - TSCH_STATS_FIRST_CHANNEL] =
clock_seconds();
277 tsch_hopping_sequence[position] = replacement;
280 tsch_cs_current_bitmap = tsch_cs_bitmap_calc();
291 LOG_DBG(
"cs: no changes\n");
303 if(!tsch_is_coordinator) {
312 index = tsch_stats_channel_to_index(updated_channel);
314 old_is_busy = (old_busyness_metric < TSCH_CS_FREE_THRESHOLD);
315 new_is_busy = (tsch_stats.channel_free_ewma[index] < TSCH_CS_FREE_THRESHOLD);
317 if(old_is_busy != new_is_busy) {
319 recaculation_requested =
true;
321 }
else if(new_is_busy) {
323 if(tsch_cs_bitmap_contains(tsch_cs_current_bitmap, updated_channel)) {
325 recaculation_requested =
true;
unsigned long clock_seconds(void)
Get the current value of the platform seconds.
void tsch_cs_adaptations_init(void)
Initializes the TSCH hopping sequence selection module.
bool tsch_cs_process(void)
Potentially update the TSCH hopping sequence.
Main API declarations for TSCH.
Header file for TSCH statistics
Header file for TSCH adaptive channel selection
Header file for the logging system
void tsch_cs_channel_stats_updated(uint8_t updated_channel, uint16_t old_busyness_metric)
Signal the need to potentially update the TSCH hopping sequence.