diff --git a/core/net/mac/tsch/tsch-adaptive-timesync.c b/core/net/mac/tsch/tsch-adaptive-timesync.c index 0b4601621..08d4df2ee 100644 --- a/core/net/mac/tsch/tsch-adaptive-timesync.c +++ b/core/net/mac/tsch/tsch-adaptive-timesync.c @@ -86,12 +86,16 @@ timesync_entry_add(int32_t val, uint32_t time_delta) static void timesync_learn_drift_ticks(uint32_t time_delta_asn, int32_t drift_ticks) { - /* should fit in 32-bit unsigned integer */ - uint32_t time_delta_ticks = time_delta_asn * tsch_timing[tsch_ts_timeslot_length]; + /* should fit in a 32-bit integer */ + int32_t time_delta_ticks = time_delta_asn * tsch_timing[tsch_ts_timeslot_length]; int32_t real_drift_ticks = drift_ticks + compensated_ticks; int32_t last_drift_ppm = (int32_t)((int64_t)real_drift_ticks * TSCH_DRIFT_UNIT / time_delta_ticks); drift_ppm = timesync_entry_add(last_drift_ppm, time_delta_ticks); + + TSCH_LOG_ADD(tsch_log_message, + snprintf(log->message, sizeof(log->message), + "drift %ld", drift_ppm / 256)); } /*---------------------------------------------------------------------------*/ /* Either reset or update the neighbor's drift */ diff --git a/core/net/mac/tsch/tsch-conf.h b/core/net/mac/tsch/tsch-conf.h index 8e091a235..d8d58f0ff 100644 --- a/core/net/mac/tsch/tsch-conf.h +++ b/core/net/mac/tsch/tsch-conf.h @@ -53,6 +53,8 @@ #define TSCH_HOPPING_SEQUENCE_4_16 (uint8_t[]){ 20, 26, 25, 26, 15, 15, 25, 20, 26, 15, 26, 25, 20, 15, 20, 25 } /* 4 channels, sequence length 4 */ #define TSCH_HOPPING_SEQUENCE_4_4 (uint8_t[]){ 15, 25, 26, 20 } +/* 2 channels, sequence length 2 */ +#define TSCH_HOPPING_SEQUENCE_2_2 (uint8_t[]){ 20, 25 } /* 1 channel, sequence length 1 */ #define TSCH_HOPPING_SEQUENCE_1_1 (uint8_t[]){ 20 } @@ -120,7 +122,7 @@ #define TSCH_DEFAULT_TS_TIMESLOT_LENGTH 10000 #elif TSCH_CONF_DEFAULT_TIMESLOT_LENGTH == 15000 -/* Default timeslot timing for platfroms requiring 15ms slots */ +/* Default timeslot timing for platforms requiring 15ms slots */ #define TSCH_DEFAULT_TS_CCA_OFFSET 1800 #define TSCH_DEFAULT_TS_CCA 128 @@ -182,4 +184,18 @@ #define TSCH_HW_FRAME_FILTERING 1 #endif /* TSCH_CONF_HW_FRAME_FILTERING */ +/* Keep radio always on within TSCH timeslot (1) or turn it off between packet and ACK? (0) */ +#ifdef TSCH_CONF_RADIO_ON_DURING_TIMESLOT +#define TSCH_RADIO_ON_DURING_TIMESLOT TSCH_CONF_RADIO_ON_DURING_TIMESLOT +#else +#define TSCH_RADIO_ON_DURING_TIMESLOT 0 +#endif + +/* How long to scan each channel in the scanning phase */ +#ifdef TSCH_CONF_CHANNEL_SCAN_DURATION +#define TSCH_CHANNEL_SCAN_DURATION TSCH_CONF_CHANNEL_SCAN_DURATION +#else +#define TSCH_CHANNEL_SCAN_DURATION CLOCK_SECOND +#endif + #endif /* __TSCH_CONF_H__ */ diff --git a/core/net/mac/tsch/tsch-queue.h b/core/net/mac/tsch/tsch-queue.h index c350ab063..676619a3a 100644 --- a/core/net/mac/tsch/tsch-queue.h +++ b/core/net/mac/tsch/tsch-queue.h @@ -39,6 +39,7 @@ #include "lib/ringbufindex.h" #include "net/linkaddr.h" #include "net/mac/tsch/tsch-schedule.h" +#include "net/mac/mac.h" /******** Configuration *******/ diff --git a/core/net/mac/tsch/tsch-rpl.c b/core/net/mac/tsch/tsch-rpl.c index 960f757f6..4558c4366 100644 --- a/core/net/mac/tsch/tsch-rpl.c +++ b/core/net/mac/tsch/tsch-rpl.c @@ -73,7 +73,7 @@ tsch_rpl_callback_leaving_network(void) } /*---------------------------------------------------------------------------*/ /* Set TSCH EB period based on current RPL DIO period. - * To use, set #define RPL_CALLBACK_PARENT_SWITCH tsch_rpl_callback_new_dio_interval */ + * To use, set #define RPL_CALLBACK_NEW_DIO_INTERVAL tsch_rpl_callback_new_dio_interval */ void tsch_rpl_callback_new_dio_interval(uint8_t dio_interval) { diff --git a/core/net/mac/tsch/tsch-slot-operation.c b/core/net/mac/tsch/tsch-slot-operation.c index 5f0701895..be83da4ab 100644 --- a/core/net/mac/tsch/tsch-slot-operation.c +++ b/core/net/mac/tsch/tsch-slot-operation.c @@ -36,6 +36,7 @@ * \author * Simon Duquennoy * Beshr Al Nahas + * Atis Elsts * */ @@ -109,6 +110,18 @@ #define RTIMER_GUARD 2u #endif +enum tsch_radio_state_on_cmd { + TSCH_RADIO_CMD_ON_START_OF_TIMESLOT, + TSCH_RADIO_CMD_ON_WITHIN_TIMESLOT, + TSCH_RADIO_CMD_ON_FORCE, +}; + +enum tsch_radio_state_off_cmd { + TSCH_RADIO_CMD_OFF_END_OF_TIMESLOT, + TSCH_RADIO_CMD_OFF_WITHIN_TIMESLOT, + TSCH_RADIO_CMD_OFF_FORCE, +}; + /* A ringbuf storing outgoing packets after they were dequeued. * Will be processed layer by tsch_tx_process_pending */ struct ringbufindex dequeued_ringbuf; @@ -370,6 +383,68 @@ update_neighbor_state(struct tsch_neighbor *n, struct tsch_packet *p, return in_queue; } /*---------------------------------------------------------------------------*/ +/** + * This function turns on the radio. Its semantics is dependent on + * the value of TSCH_RADIO_ON_DURING_TIMESLOT constant: + * - if enabled, the radio is turned on at the start of the slot + * - if disabled, the radio is turned on within the slot, + * directly before the packet Rx guard time and ACK Rx guard time. + */ +static void +tsch_radio_on(enum tsch_radio_state_on_cmd command) +{ + int do_it = 0; + switch(command) { + case TSCH_RADIO_CMD_ON_START_OF_TIMESLOT: + if(TSCH_RADIO_ON_DURING_TIMESLOT) { + do_it = 1; + } + break; + case TSCH_RADIO_CMD_ON_WITHIN_TIMESLOT: + if(!TSCH_RADIO_ON_DURING_TIMESLOT) { + do_it = 1; + } + break; + case TSCH_RADIO_CMD_ON_FORCE: + do_it = 1; + break; + } + if(do_it) { + NETSTACK_RADIO.on(); + } +} +/*---------------------------------------------------------------------------*/ +/** + * This function turns off the radio. In the same way as for tsch_radio_on(), + * it depends on the value of TSCH_RADIO_ON_DURING_TIMESLOT constant: + * - if enabled, the radio is turned off at the end of the slot + * - if disabled, the radio is turned off within the slot, + * directly after Tx'ing or Rx'ing a packet or Tx'ing an ACK. + */ +static void +tsch_radio_off(enum tsch_radio_state_off_cmd command) +{ + int do_it = 0; + switch(command) { + case TSCH_RADIO_CMD_OFF_END_OF_TIMESLOT: + if(TSCH_RADIO_ON_DURING_TIMESLOT) { + do_it = 1; + } + break; + case TSCH_RADIO_CMD_OFF_WITHIN_TIMESLOT: + if(!TSCH_RADIO_ON_DURING_TIMESLOT) { + do_it = 1; + } + break; + case TSCH_RADIO_CMD_OFF_FORCE: + do_it = 1; + break; + } + if(do_it) { + NETSTACK_RADIO.off(); + } +} +/*---------------------------------------------------------------------------*/ static PT_THREAD(tsch_tx_slot(struct pt *pt, struct rtimer *t)) { @@ -456,7 +531,7 @@ PT_THREAD(tsch_tx_slot(struct pt *pt, struct rtimer *t)) /* delay before CCA */ TSCH_SCHEDULE_AND_YIELD(pt, t, current_slot_start, TS_CCA_OFFSET, "cca"); TSCH_DEBUG_TX_EVENT(); - NETSTACK_RADIO.on(); + tsch_radio_on(TSCH_RADIO_CMD_ON_WITHIN_TIMESLOT); /* CCA */ BUSYWAIT_UNTIL_ABS(!(cca_status |= NETSTACK_RADIO.channel_clear()), current_slot_start, TS_CCA_OFFSET + TS_CCA); @@ -480,7 +555,7 @@ PT_THREAD(tsch_tx_slot(struct pt *pt, struct rtimer *t)) /* limit tx_time to its max value */ tx_duration = MIN(tx_duration, tsch_timing[tsch_ts_max_tx]); /* turn tadio off -- will turn on again to wait for ACK if needed */ - NETSTACK_RADIO.off(); + tsch_radio_off(TSCH_RADIO_CMD_OFF_WITHIN_TIMESLOT); if(mac_tx_status == RADIO_TX_OK) { if(!is_broadcast) { @@ -488,12 +563,12 @@ PT_THREAD(tsch_tx_slot(struct pt *pt, struct rtimer *t)) int ack_len; rtimer_clock_t ack_start_time; int is_time_source; - radio_value_t radio_rx_mode; struct ieee802154_ies ack_ies; uint8_t ack_hdrlen; frame802154_t frame; #if TSCH_HW_FRAME_FILTERING + radio_value_t radio_rx_mode; /* Entering promiscuous mode so that the radio accepts the enhanced ACK */ NETSTACK_RADIO.get_value(RADIO_PARAM_RX_MODE, &radio_rx_mode); NETSTACK_RADIO.set_value(RADIO_PARAM_RX_MODE, radio_rx_mode & (~RADIO_RX_MODE_ADDRESS_FILTER)); @@ -502,7 +577,7 @@ PT_THREAD(tsch_tx_slot(struct pt *pt, struct rtimer *t)) TSCH_SCHEDULE_AND_YIELD(pt, t, current_slot_start, tsch_timing[tsch_ts_tx_offset] + tx_duration + tsch_timing[tsch_ts_rx_ack_delay] - RADIO_DELAY_BEFORE_RX, "TxBeforeAck"); TSCH_DEBUG_TX_EVENT(); - NETSTACK_RADIO.on(); + tsch_radio_on(TSCH_RADIO_CMD_ON_WITHIN_TIMESLOT); /* Wait for ACK to come */ BUSYWAIT_UNTIL_ABS(NETSTACK_RADIO.receiving_packet(), tx_start_time, tx_duration + tsch_timing[tsch_ts_rx_ack_delay] + tsch_timing[tsch_ts_ack_wait]); @@ -514,7 +589,7 @@ PT_THREAD(tsch_tx_slot(struct pt *pt, struct rtimer *t)) BUSYWAIT_UNTIL_ABS(!NETSTACK_RADIO.receiving_packet(), ack_start_time, tsch_timing[tsch_ts_max_ack]); TSCH_DEBUG_TX_EVENT(); - NETSTACK_RADIO.off(); + tsch_radio_off(TSCH_RADIO_CMD_OFF_WITHIN_TIMESLOT); #if TSCH_HW_FRAME_FILTERING /* Leaving promiscuous mode */ @@ -588,6 +663,8 @@ PT_THREAD(tsch_tx_slot(struct pt *pt, struct rtimer *t)) } } + tsch_radio_off(TSCH_RADIO_CMD_OFF_END_OF_TIMESLOT); + current_packet->transmissions++; current_packet->ret = mac_tx_status; @@ -671,32 +748,26 @@ PT_THREAD(tsch_rx_slot(struct pt *pt, struct rtimer *t)) TSCH_DEBUG_RX_EVENT(); /* Start radio for at least guard time */ - NETSTACK_RADIO.on(); - packet_seen = NETSTACK_RADIO.receiving_packet(); + tsch_radio_on(TSCH_RADIO_CMD_ON_WITHIN_TIMESLOT); + packet_seen = NETSTACK_RADIO.receiving_packet() || NETSTACK_RADIO.pending_packet(); if(!packet_seen) { /* Check if receiving within guard time */ BUSYWAIT_UNTIL_ABS((packet_seen = NETSTACK_RADIO.receiving_packet()), current_slot_start, tsch_timing[tsch_ts_rx_offset] + tsch_timing[tsch_ts_rx_wait]); } - if(packet_seen) { + if(!packet_seen) { + /* no packets on air */ + tsch_radio_off(TSCH_RADIO_CMD_OFF_FORCE); + } else { TSCH_DEBUG_RX_EVENT(); /* Save packet timestamp */ rx_start_time = RTIMER_NOW() - RADIO_DELAY_BEFORE_DETECT; - } - if(!NETSTACK_RADIO.receiving_packet() && !NETSTACK_RADIO.pending_packet()) { - NETSTACK_RADIO.off(); - /* no packets on air */ - } else { + /* Wait until packet is received, turn radio off */ BUSYWAIT_UNTIL_ABS(!NETSTACK_RADIO.receiving_packet(), current_slot_start, tsch_timing[tsch_ts_rx_offset] + tsch_timing[tsch_ts_rx_wait] + tsch_timing[tsch_ts_max_tx]); TSCH_DEBUG_RX_EVENT(); - NETSTACK_RADIO.off(); - -#if TSCH_RESYNC_WITH_SFD_TIMESTAMPS - /* At the end of the reception, get an more accurate estimate of SFD arrival time */ - NETSTACK_RADIO.get_object(RADIO_PARAM_LAST_PACKET_TIMESTAMP, &rx_start_time, sizeof(rtimer_clock_t)); -#endif + tsch_radio_off(TSCH_RADIO_CMD_OFF_WITHIN_TIMESLOT); if(NETSTACK_RADIO.pending_packet()) { static int frame_valid; @@ -704,9 +775,9 @@ PT_THREAD(tsch_rx_slot(struct pt *pt, struct rtimer *t)) static frame802154_t frame; radio_value_t radio_last_rssi; - NETSTACK_RADIO.get_value(RADIO_PARAM_LAST_RSSI, &radio_last_rssi); /* Read packet */ current_input->len = NETSTACK_RADIO.read((void *)current_input->payload, TSCH_PACKET_MAX_LEN); + NETSTACK_RADIO.get_value(RADIO_PARAM_LAST_RSSI, &radio_last_rssi); current_input->rx_asn = current_asn; current_input->rssi = (signed)radio_last_rssi; header_len = frame802154_parse((uint8_t *)current_input->payload, current_input->len, &frame); @@ -714,6 +785,11 @@ PT_THREAD(tsch_rx_slot(struct pt *pt, struct rtimer *t)) frame802154_check_dest_panid(&frame) && frame802154_extract_linkaddr(&frame, &source_address, &destination_address); +#if TSCH_RESYNC_WITH_SFD_TIMESTAMPS + /* At the end of the reception, get an more accurate estimate of SFD arrival time */ + NETSTACK_RADIO.get_object(RADIO_PARAM_LAST_PACKET_TIMESTAMP, &rx_start_time, sizeof(rtimer_clock_t)); +#endif + packet_duration = TSCH_PACKET_DURATION(current_input->len); #if LLSEC802154_ENABLED @@ -745,7 +821,7 @@ PT_THREAD(tsch_rx_slot(struct pt *pt, struct rtimer *t)) #if TSCH_TIMESYNC_REMOVE_JITTER /* remove jitter due to measurement errors */ - if(abs(estimated_drift) <= TSCH_TIMESYNC_MEASUREMENT_ERROR) { + if(ABS(estimated_drift) <= TSCH_TIMESYNC_MEASUREMENT_ERROR) { estimated_drift = 0; } else if(estimated_drift > 0) { estimated_drift -= TSCH_TIMESYNC_MEASUREMENT_ERROR; @@ -784,7 +860,7 @@ PT_THREAD(tsch_rx_slot(struct pt *pt, struct rtimer *t)) packet_duration + tsch_timing[tsch_ts_tx_ack_delay] - RADIO_DELAY_BEFORE_TX, "RxBeforeAck"); TSCH_DEBUG_RX_EVENT(); NETSTACK_RADIO.transmit(ack_len); - NETSTACK_RADIO.off(); + tsch_radio_off(TSCH_RADIO_CMD_OFF_WITHIN_TIMESLOT); } /* If the sender is a time source, proceed to clock drift compensation */ @@ -820,6 +896,8 @@ PT_THREAD(tsch_rx_slot(struct pt *pt, struct rtimer *t)) process_poll(&tsch_pending_events_process); } } + + tsch_radio_off(TSCH_RADIO_CMD_OFF_END_OF_TIMESLOT); } if(input_queue_drop != 0) { @@ -860,8 +938,12 @@ PT_THREAD(tsch_slot_operation(struct rtimer *t, void *ptr)) } else { uint8_t current_channel; + int is_active_slot; TSCH_DEBUG_SLOT_START(); tsch_in_slot_operation = 1; + /* Reset drift correction */ + drift_correction = 0; + is_drift_correction_used = 0; /* Get a packet ready to be sent */ current_packet = get_packet_and_neighbor_for_link(current_link, ¤t_neighbor); /* There is no packet to send, and this link does not have Rx flag. Instead of doing @@ -870,26 +952,28 @@ PT_THREAD(tsch_slot_operation(struct rtimer *t, void *ptr)) current_link = backup_link; current_packet = get_packet_and_neighbor_for_link(current_link, ¤t_neighbor); } - /* Hop channel */ - current_channel = tsch_calculate_channel(¤t_asn, current_link->channel_offset); - NETSTACK_RADIO.set_value(RADIO_PARAM_CHANNEL, current_channel); - /* Reset drift correction */ - drift_correction = 0; - is_drift_correction_used = 0; - /* Decide whether it is a TX/RX/IDLE or OFF slot */ - /* Actual slot operation */ - if(current_packet != NULL) { - /* We have something to transmit, do the following: - * 1. send - * 2. update_backoff_state(current_neighbor) - * 3. post tx callback - **/ - static struct pt slot_tx_pt; - PT_SPAWN(&slot_operation_pt, &slot_tx_pt, tsch_tx_slot(&slot_tx_pt, t)); - } else if((current_link->link_options & LINK_OPTION_RX)) { - /* Listen */ - static struct pt slot_rx_pt; - PT_SPAWN(&slot_operation_pt, &slot_rx_pt, tsch_rx_slot(&slot_rx_pt, t)); + is_active_slot = current_packet != NULL || (current_link->link_options & LINK_OPTION_RX); + if(is_active_slot) { + /* Hop channel */ + current_channel = tsch_calculate_channel(¤t_asn, current_link->channel_offset); + NETSTACK_RADIO.set_value(RADIO_PARAM_CHANNEL, current_channel); + /* Turn the radio on already here if configured so; necessary for radios with slow startup */ + tsch_radio_on(TSCH_RADIO_CMD_ON_START_OF_TIMESLOT); + /* Decide whether it is a TX/RX/IDLE or OFF slot */ + /* Actual slot operation */ + if(current_packet != NULL) { + /* We have something to transmit, do the following: + * 1. send + * 2. update_backoff_state(current_neighbor) + * 3. post tx callback + **/ + static struct pt slot_tx_pt; + PT_SPAWN(&slot_operation_pt, &slot_tx_pt, tsch_tx_slot(&slot_tx_pt, t)); + } else { + /* Listen */ + static struct pt slot_rx_pt; + PT_SPAWN(&slot_operation_pt, &slot_rx_pt, tsch_rx_slot(&slot_rx_pt, t)); + } } TSCH_DEBUG_SLOT_END(); } diff --git a/core/net/mac/tsch/tsch.c b/core/net/mac/tsch/tsch.c index 2085f39ff..292744ecc 100644 --- a/core/net/mac/tsch/tsch.c +++ b/core/net/mac/tsch/tsch.c @@ -609,24 +609,25 @@ PT_THREAD(tsch_scan(struct pt *pt)) static struct input_packet input_eb; static struct etimer scan_timer; + /* Time when we started scanning on current_channel */ + static clock_time_t current_channel_since; ASN_INIT(current_asn, 0, 0); etimer_set(&scan_timer, CLOCK_SECOND / TSCH_ASSOCIATION_POLL_FREQUENCY); + current_channel_since = clock_time(); while(!tsch_is_associated && !tsch_is_coordinator) { /* Hop to any channel offset */ - static int current_channel = 0; - /* Time when we started scanning on current_channel */ - static clock_time_t current_channel_since = 0; + static uint8_t current_channel = 0; /* We are not coordinator, try to associate */ rtimer_clock_t t0; int is_packet_pending = 0; - clock_time_t now_seconds = clock_seconds(); + clock_time_t now_time = clock_time(); /* Switch to a (new) channel for scanning */ - if(current_channel == 0 || now_seconds != current_channel_since) { + if(current_channel == 0 || now_time - current_channel_since > TSCH_CHANNEL_SCAN_DURATION) { /* Pick a channel at random in TSCH_JOIN_HOPPING_SEQUENCE */ uint8_t scan_channel = TSCH_JOIN_HOPPING_SEQUENCE[ random_rand() % sizeof(TSCH_JOIN_HOPPING_SEQUENCE)]; @@ -635,7 +636,7 @@ PT_THREAD(tsch_scan(struct pt *pt)) current_channel = scan_channel; PRINTF("TSCH: scanning on channel %u\n", scan_channel); } - current_channel_since = now_seconds; + current_channel_since = now_time; } /* Turn radio on and wait for EB */ @@ -649,12 +650,12 @@ PT_THREAD(tsch_scan(struct pt *pt)) } if(is_packet_pending) { - /* Save packet timestamp */ - NETSTACK_RADIO.get_object(RADIO_PARAM_LAST_PACKET_TIMESTAMP, &t0, sizeof(rtimer_clock_t)); - /* Read packet */ input_eb.len = NETSTACK_RADIO.read(input_eb.payload, TSCH_PACKET_MAX_LEN); + /* Save packet timestamp */ + NETSTACK_RADIO.get_object(RADIO_PARAM_LAST_PACKET_TIMESTAMP, &t0, sizeof(rtimer_clock_t)); + /* Parse EB and attempt to associate */ PRINTF("TSCH: association: received packet (%u bytes) on channel %u\n", input_eb.len, current_channel);