Hello! I am still fairly new to UWB communication and have been trying to change a double-sided two-way ranging system into a single-sided system. I am running into some issues accounting properly for delays and complete reception of each frame. The defined timeouts and delays in the following snippets have not been changed from the double-sided system.
This code snippet is common to both my transmitter and responder:
// Here is the data included in the poll message and the response message:
static uint8 rx_poll_msg = {0x41, 0x88, 0, 0xCA, 0xDE, ‘W’, ‘A’, ‘V’, ‘E’, 0x21, 0, 0};
static uint8 rx_resp_msg = {0x41, 0x88, 0, 0xCA, 0xDE, ‘V’, ‘E’, ‘W’, ‘A’, 0x10, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
// This is where the timestamps begin in the response message:
#define RESP_MSG_POLL_RX_TS_IDX 13
#define RESP_MSG_RESP_TX_TS_IDX 17
// This is how we account for conversion between microseconds and device time units
// UWB microsecond (uus) to device time unit (dtu, around 15.65 ps) conversion factor
// 1 uus = 512 / 499.2 �s and 1 �s = 499.2 * 128 dtu
#define UUS_TO_DWT_TIME 65536
// This is how the speed of light is defined
#define SPEED_OF_LIGHT 299702547
This code snippet is on the transmitter side as I prepare and send the poll message:
/* Delay between frames, in UWB microseconds./
/ This is the delay from the end of the frame transmission to the enable of the receiver, as programmed for the DW1000’s wait for response feature. */
#define POLL_TX_TO_RESP_RX_DLY_UUS 100
/* This is the delay from Frame RX timestamp to TX reply timestamp used for calculating/setting the DW1000’s delayed TX function. This includes the frame length of approximately 2.66 ms with above configuration. */
#define RESP_RX_TO_FINAL_TX_DLY_UUS 3100
/* Receive response timeout */
#define RESP_RX_TIMEOUT_UUS 2700
/* Preamble timeout, in multiple of PAC size */
#define PRE_TIMEOUT 0 (this is 0 so the preamble timeout isn’t enabled)
/* Write frame data to DW1000 and prepare transmission. /
tx_poll_msg[ALL_MSG_SN_IDX] = frame_seq_nb;
dwt_writetxdata(sizeof(tx_poll_msg), tx_poll_msg, 0); / Zero offset in TX buffer. /
dwt_writetxfctrl(sizeof(tx_poll_msg), 0, 1); / Zero offset in TX buffer, ranging. /
/ Start transmission, indicating that a response is expected so that reception is enabled automatically after the frame is sent and the delay
* set by dwt_setrxaftertxdelay() has elapsed. /
ret1 = dwt_starttx(DWT_START_TX_IMMEDIATE | DWT_RESPONSE_EXPECTED);
/ If dwt_starttx() returns an error, abandon this ranging exchange and proceed to the next one. See NOTE 12 below. /
if (ret1 == DWT_SUCCESS)
{
/ Poll DW1000 until TX frame sent event set. See NOTE 9 below. /
while (!(dwt_read32bitreg(SYS_STATUS_ID) & SYS_STATUS_TXFRS))
{};
/ Clear TXFRS event. */
dwt_write32bitreg(SYS_STATUS_ID, SYS_STATUS_TXFRS);
// Increment frame sequence number after transmission of the poll message (modulo 256). /
frame_seq_nb++;
GreLED_on();
osDelay(50);
GreLED_off();
}
/ Increment frame sequence number after transmission of the poll message (modulo 256). */
frame_seq_nb++;
ret2 = dwt_rxenable(DWT_START_RX_IMMEDIATE);
This code snippet is on the receiver side as I intercept the poll message, check that it is correct, and prepare and send the response message:
/* Delay between frames, in UWB microseconds. /
/ This is the delay from Frame RX timestamp to TX reply timestamp used for calculating/setting the DW1000’s delayed TX function. This includes the frame length of approximately 2.46 ms with above configuration. */
#define POLL_RX_TO_RESP_TX_DLY_UUS 2750
/* This is the delay from the end of the frame transmission to the enable of the receiver, as programmed for the DW1000’s wait for response feature. */
#define RESP_TX_TO_FINAL_RX_DLY_UUS 500
/* Receive final timeout. */
#define FINAL_RX_TIMEOUT_UUS 3300
/* Preamble timeout, in multiple of PAC size.*/
#define PRE_TIMEOUT 0
// The response message contains the poll rx timestamp (recorded at the detection of the preamble code) // and the response transmission timestamp based off of the following calculation:
resp_tx_time = (poll_rx_ts + (POLL_RX_TO_RESP_TX_DLY_UUS * UUS_TO_DWT_TIME)) >> 8;
resp_tx_ts = resp_tx_time + poll_rx_ts;
// This is how I am preparing to respond and how I am sending the response message:
// First, I check that the frame is a poll sent by “DS TWR initiator” example.
// As the sequence number field of the frame is not relevant, it is cleared to simplify the validation of the frame:
rx_buffer[ALL_MSG_SN_IDX] = 0;
if (memcmp(rx_buffer, rx_poll_msg, ALL_MSG_COMMON_LEN) == 0)
{
uint32 resp_tx_time = 0;;
int ret;
/* Retrieve poll reception timestamp. /
poll_rx_ts = get_rx_timestamp_u64();
/ Set send time for response. See NOTE 9 below. /
resp_tx_time = (poll_rx_ts + (POLL_RX_TO_RESP_TX_DLY_UUS * UUS_TO_DWT_TIME)) >> 8;
dwt_setdelayedtrxtime(resp_tx_time);
// Compute resp_tx_ts
resp_tx_ts = resp_tx_time + poll_rx_ts;
// Set timestamp in resp msg
resp_msg_set_ts(&rx_buffer[RESP_MSG_POLL_RX_TS_IDX], poll_rx_ts);
resp_msg_set_ts(&rx_buffer[RESP_MSG_RESP_TX_TS_IDX], resp_tx_ts);
/ Set expected delay and timeout for final message reception. See NOTE 4 and 5 below. /
dwt_setrxaftertxdelay(RESP_TX_TO_FINAL_RX_DLY_UUS);
dwt_setrxtimeout(FINAL_RX_TIMEOUT_UUS);
/ Write and send the response message. See NOTE 10 below./
tx_resp_msg[ALL_MSG_SN_IDX] = frame_seq_nb;
dwt_writetxdata(sizeof(tx_resp_msg), tx_resp_msg, 0); / Zero offset in TX buffer. /
dwt_writetxfctrl(sizeof(tx_resp_msg), 0, 1); / Zero offset in TX buffer, ranging. /
ret = dwt_starttx(DWT_START_TX_DELAYED);
if (ret == DWT_SUCCESS)
{
/ Poll DW1000 until TX frame sent event set. See NOTE 9 below. /
while (!(dwt_read32bitreg(SYS_STATUS_ID) & SYS_STATUS_TXFRS))
{};
/ Clear TXFRS event. */
dwt_write32bitreg(SYS_STATUS_ID, SYS_STATUS_TXFRS);
// Increment frame sequence number after transmission of the poll message (modulo 256). /
frame_seq_nb++;
GreLED_on();
osDelay(50);
GreLED_off();
}
/ If dwt_starttx() returns an error, abandon this ranging exchange and proceed to the next one. */
if (ret == DWT_ERROR)
{
RedLED_on();
osDelay(50);
RedLED_off();
}
}
Now, this code snippet is back on the transmitter side as I receive the response message, check that it is correct, and compute the final ToF. The end behavior I’d like is the initiator to turn on the green LED and it to be in a wait state until I reset the module:
/* Check that the frame is the expected response from the companion “DS TWR responder” example.
* As the sequence number field of the frame is not relevant, it is cleared to simplify the validation of the frame. /
rx_buffer[ALL_MSG_SN_IDX] = 0;
if (memcmp(rx_buffer, rx_resp_msg, ALL_MSG_COMMON_LEN) == 0)
{
uint32 poll_rx_ts = 0;
uint32 resp_tx_ts = 0;
uint32 poll_rx_ts_32 = 0;
uint32 resp_tx_ts_32 = 0;
double t_reply = 0;
int64 tof_dtu = 0;
double tof = 0;
double distance = 0;
// Get timestamps embedded in response message
resp_msg_get_ts(&rx_buffer[RESP_MSG_POLL_RX_TS_IDX], &poll_rx_ts);
resp_msg_get_ts(&rx_buffer[RESP_MSG_RESP_TX_TS_IDX], &resp_tx_ts);
/ Retrieve poll transmission and response reception timestamp. */
poll_tx_ts = get_tx_timestamp_u64();
resp_rx_ts = get_rx_timestamp_u64();
// Perform 32 bit subtractions to compute correct timestamps if clock has wrapped
poll_rx_ts_32 = (uint32)poll_rx_ts;
resp_tx_ts_32 = (uint32)resp_tx_ts;
// Compute t_reply by subtracting resp_tx_ts from poll_rx_ts
t_reply = (double)(resp_tx_ts - poll_rx_ts);
// Compute ToF in device time units
tof_dtu = (uint64)((resp_rx_ts - poll_tx_ts - t_reply)/2);
// Convert ToF into ordinary time
tof = tof_dtu * DWT_TIME_UNITS;
// Multiply ToF by speed of light to compute the distance
distance = tof * SPEED_OF_LIGHT;
// Turn on green led and turn off red LED to indicate that distance has been computed and ranging is done
GreLED_on();
while (1)
{
osDelay(100);
}
}
I am getting the transmitter to properly send the poll message, the receiver will intercept that message, and properly send the response message back to the transmitter. However, I have yet to detect the response message on the transmitter side. I have double-checked that the response message is being fully sent and that the transmitter goes into receive mode after the coded delay of 100 microseconds before listening for the response message.
I have determined that I must be running into a timing issue and that is why my ranging exchange seems to be failing. I’d like to mathematically determine how much delay I need and what the timestamp values should be given the length of the message being sent. How may I do this and ensure that I am doing the right things to achieve single-sided two-way ranging?
Thank you so much for any suggestions
P.S. My responder doesn’t always succeed in the transmission of the response message. I think this may be because my POLL_RX_TO_RESP_TX_DLY_UUS may not be long enough. Any insight on this issue? Thanks again!