42 #error tsch-cs requires tsch-stats. Please enable TSCH_STATS_CONF_ON. 45 #if ! TSCH_STATS_SAMPLE_NOISE_RSSI 46 #error tsch-cs requires periodic RSSI sampling. Please enable TSCH_STATS_CONF_SAMPLE_NOISE_RSSI. 51 #define LOG_MODULE "TSCH CS" 52 #define LOG_LEVEL LOG_LEVEL_MAC 57 #define TSCH_CS_MAX_CHANNELS_CHANGED 1 60 #define TSCH_CS_MIN_UPDATE_INTERVAL_SEC 60 63 #define TSCH_CS_HYSTERESIS (TSCH_STATS_BINARY_SCALING_FACTOR / 10) 66 #define TSCH_CS_BLACKLIST_DURATION_SEC (5 * 60) 69 static bool recaculation_requested;
72 static uint32_t tsch_cs_busy_since[TSCH_STATS_NUM_CHANNELS];
80 static tsch_cs_bitmap_t tsch_cs_initial_bitmap;
82 static tsch_cs_bitmap_t tsch_cs_current_bitmap;
85 struct tsch_cs_quality {
93 tsch_cs_bitmap_contains(tsch_cs_bitmap_t bitmap, uint8_t channel)
95 return (1 << (channel - TSCH_STATS_FIRST_CHANNEL)) & bitmap;
98 static inline tsch_cs_bitmap_t
99 tsch_cs_bitmap_set(tsch_cs_bitmap_t bitmap, uint8_t channel)
101 return (1 << (channel - TSCH_STATS_FIRST_CHANNEL)) | bitmap;
104 static tsch_cs_bitmap_t
105 tsch_cs_bitmap_calc(
void)
107 tsch_cs_bitmap_t result = 0;
109 for(i = 0; i < tsch_hopping_sequence_length.val; ++i) {
110 result = tsch_cs_bitmap_set(result, tsch_hopping_sequence[i]);
118 tsch_cs_initial_bitmap = tsch_cs_bitmap_calc();
119 tsch_cs_current_bitmap = tsch_cs_initial_bitmap;
124 tsch_cs_bubble_sort(
struct tsch_cs_quality *qualities)
127 struct tsch_cs_quality tmp;
129 for(i = 0; i < TSCH_STATS_NUM_CHANNELS; ++i) {
130 for(j = 0; j + 1 < TSCH_STATS_NUM_CHANNELS; ++j) {
131 if(qualities[j].metric < qualities[j+1].metric){
133 qualities[j] = qualities[j+1];
134 qualities[j+1] = tmp;
142 tsch_cs_select_replacement(uint8_t old_channel, tsch_stat_t old_ewma,
143 struct tsch_cs_quality *qualities, uint8_t is_in_sequence[])
147 tsch_cs_bitmap_t bitmap = tsch_cs_bitmap_set(0, old_channel);
150 old_ewma += TSCH_CS_HYSTERESIS;
153 for(i = 0; i < TSCH_STATS_NUM_CHANNELS - 1; ++i) {
155 uint8_t candidate = qualities[i].channel;
157 if(qualities[i].metric < TSCH_CS_FREE_THRESHOLD) {
162 LOG_DBG(
"ch %u: busy\n", candidate);
166 if(qualities[i].metric < old_ewma) {
168 LOG_DBG(
"ch %u: hysteresis check failed\n", candidate);
173 if(is_in_sequence[candidate - TSCH_STATS_FIRST_CHANNEL] != 0xff) {
174 LOG_DBG(
"ch %u: in seq\n", candidate);
179 if(tsch_cs_busy_since[candidate - TSCH_STATS_FIRST_CHANNEL] != 0
180 && tsch_cs_busy_since[candidate - TSCH_STATS_FIRST_CHANNEL] + TSCH_CS_BLACKLIST_DURATION_SEC > now) {
181 LOG_DBG(
"ch %u: recent bl\n", candidate);
186 if(bitmap == (tsch_cs_initial_bitmap & tsch_cs_current_bitmap)) {
188 if(!tsch_cs_bitmap_contains(tsch_cs_initial_bitmap, candidate)) {
206 struct tsch_cs_quality qualities[TSCH_STATS_NUM_CHANNELS];
207 uint8_t is_channel_busy[TSCH_STATS_NUM_CHANNELS];
208 uint8_t is_in_sequence[TSCH_STATS_NUM_CHANNELS];
209 static uint32_t last_time_changed;
211 if(!recaculation_requested) {
216 if(last_time_changed != 0 && last_time_changed + TSCH_CS_MIN_UPDATE_INTERVAL_SEC >
clock_seconds()) {
222 recaculation_requested =
false;
224 for(i = 0; i < TSCH_STATS_NUM_CHANNELS; ++i) {
225 qualities[i].channel = i + TSCH_STATS_FIRST_CHANNEL;
226 qualities[i].metric = tsch_stats.channel_free_ewma[i];
230 tsch_cs_bubble_sort(qualities);
233 for(i = 0; i < TSCH_STATS_NUM_CHANNELS; ++i) {
234 is_channel_busy[i] = (tsch_stats.channel_free_ewma[i] < TSCH_CS_FREE_THRESHOLD);
236 memset(is_in_sequence, 0xff,
sizeof(is_in_sequence));
237 for(i = 0; i < tsch_hopping_sequence_length.val; ++i) {
238 uint8_t channel = tsch_hopping_sequence[i];
239 is_in_sequence[channel - TSCH_STATS_FIRST_CHANNEL] = i;
243 for(i = 0; i < tsch_hopping_sequence_length.val; ++i) {
244 is_channel_busy[qualities[i].channel - TSCH_STATS_FIRST_CHANNEL] = 0;
247 for(i = 0; i < TSCH_STATS_NUM_CHANNELS; ++i) {
248 uint8_t ci = qualities[i].channel - TSCH_STATS_FIRST_CHANNEL;
250 LOG_DBG(
"ch %u q %u busy %u in seq %u\n",
251 qualities[i].channel,
254 is_in_sequence[ci] == 0xff ? 0 : 1);
258 for(i = 0; i < tsch_hopping_sequence_length.val; ++i) {
259 uint8_t channel = tsch_hopping_sequence[i];
260 if(is_channel_busy[channel - TSCH_STATS_FIRST_CHANNEL]) {
265 LOG_DBG(
"cs: not replacing\n");
269 has_replaced =
false;
270 for(i = TSCH_STATS_NUM_CHANNELS - 1; i >= tsch_hopping_sequence_length.val; --i) {
271 if(is_in_sequence[qualities[i].channel - TSCH_STATS_FIRST_CHANNEL] != 0xff) {
273 uint8_t channel = qualities[i].channel;
274 tsch_stat_t ewma_metric = qualities[i].metric;
275 uint8_t replacement = tsch_cs_select_replacement(channel, ewma_metric,
276 qualities, is_in_sequence);
277 uint8_t position = is_in_sequence[channel - TSCH_STATS_FIRST_CHANNEL];
279 if(replacement != 0xff) {
280 printf(
"\ncs: replacing channel %u %u (%u) with %u\n",
281 channel, tsch_hopping_sequence[position], position, replacement);
283 tsch_cs_busy_since[channel - TSCH_STATS_FIRST_CHANNEL] =
clock_seconds();
285 tsch_hopping_sequence[position] = replacement;
288 tsch_cs_current_bitmap = tsch_cs_bitmap_calc();
299 LOG_DBG(
"cs: no changes\n");
311 if(!tsch_is_coordinator) {
320 index = tsch_stats_channel_to_index(updated_channel);
322 old_is_busy = (old_busyness_metric < TSCH_CS_FREE_THRESHOLD);
323 new_is_busy = (tsch_stats.channel_free_ewma[index] < TSCH_CS_FREE_THRESHOLD);
325 if(old_is_busy != new_is_busy) {
327 recaculation_requested =
true;
329 }
else if(new_is_busy) {
331 if(tsch_cs_bitmap_contains(tsch_cs_current_bitmap, updated_channel)) {
333 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.