How to handle inaccuracy of delayed transmisson?

Some background …

Single sided TWR is depending on:

  1. total time between transmit of “poll” message in initiator (poll_tx_ts) and receive response from responder (resp_rx_ts)
  2. total time in responder between receive of poll message (poll_rx_ts) from initiator and transmit of response to initiator (resp_tx_ts).

See relevant excerpt from initiator (ex_06a_ss_twr_initiator) below.

/* Retrieve poll transmission and response reception timestamps. See NOTE 9 below. */
poll_tx_ts = dwt_readtxtimestamplo32();
resp_rx_ts = dwt_readrxtimestamplo32();
/* Read carrier integrator value and calculate clock offset ratio. See NOTE 11 below. */
clockOffsetRatio = ((float)dwt_readclockoffset()) / (uint32_t)(1<<26);
/* 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);
/* Compute time of flight and distance, using clock offset ratio to correct for differing local and remote clock rates */
rtd_init = resp_rx_ts - poll_tx_ts;
rtd_resp = resp_tx_ts - poll_rx_ts;
tof = ((rtd_init - rtd_resp * (1 - clockOffsetRatio)) / 2.0) * DWT_TIME_UNITS;
distance = tof * SPEED_OF_LIGHT;

See relevant excerpt from responder (ex_06a_ss_twr_responder) below.

/* Retrieve poll reception timestamp. */
poll_rx_ts = get_rx_timestamp_u64();
/* Compute response message transmission time. See NOTE 7 below. */
resp_tx_time = (poll_rx_ts + (POLL_RX_TO_RESP_TX_DLY_UUS * UUS_TO_DWT_TIME)) >> 8;
dwt_setdelayedtrxtime(resp_tx_time);
/* Response TX timestamp is the transmission time we programmed plus the antenna delay. */
resp_tx_ts = (((uint64_t)(resp_tx_time & 0xFFFFFFFEUL)) << 8) + TX_ANT_DLY;
/* Write all timestamps in the final message. See NOTE 8 below. */
resp_msg_set_ts(&tx_resp_msg[RESP_MSG_POLL_RX_TS_IDX], poll_rx_ts);
resp_msg_set_ts(&tx_resp_msg[RESP_MSG_RESP_TX_TS_IDX], resp_tx_ts);

Distance calculation is then simply the following (pseudo code) which is resonable.

( ( (resp_rx_ts - poll_tx_ts) - (resp_tx_ts - poll_rx_ts) ) / 2 ) * SPEED_OF_LIGHT

What I don’t understand is how the inaccuracy of delayed TX is handled.

dwt_setdelayedtrxtime() does not make use of the lower 9 bits of the total time stamp.

While the resolution of the time stamp is 15.65 ps (corresponding to 6 mm tof), the resolution of the delayed transmit is 512 times lower i.e. 8 ns (corresponding to 2.4 m tof).

I have confirmed that dwt_setdelayedtrxtime() result in a roughly even random distribution of delay within the resolution of delay of 8 ns. This means that the distance measurement may vary by 2.4 meters with an even distribution.

The actual time stamp may be retrieved by getting the time stamp AFTER transmission has completed. But as this is available after transmission in responder it cannot be included in the first response message.

I don’t see any way around this than sending an additional message (possible in a following TWR round) that contains the actual accurate TX time stamp to improve accuracy of SS-TWR.

Is this a correct interpretation?
Will SS-TWR sample produce a minimum accuracy of about 2.4 m?

The responders transmission will be sent with a timestamp ending in 9 0’s when it would ideally be sent with a timestamp a fixed offset from the time it received the message.
This is all known by the responder before it transmits. Which means it knows the error in the transmission time before transmission, it’s IDEAL_TIMESTAMP & 0x01ff. If you know what the error will be before transmission you can include this value in the message being transmitted which would then allow the other end to correct for it.
Or if (as is the case here) the ideal TX time is included in the message then the initiator can easily calculate what the error was and correct for it.

Ah, thanks for clarification! It is the receive time that is the unknown parameter, not the transmit time!
It makes sense to include receive time in message before delayed transmission is completed.

  1. Receive poll message and store reception time stamp
  2. Set delayed response time (dwt_setdelayedtrxtime) measured from received time stamp in previous step
    • Actual TX time stamp is accurate with fixed lower 9 bits
  3. Record reception time stamp in response message
  4. Wait for TX to trigger (dwt_starttx)

From my logs it seems that transmit time stamp end with 0b000010101 (0x15) not zero, but its consistent. Its also overflowing faster than I expected. But that might be due to sleep mode of DW3000 between iterations.

...
RX: 0x00031D6432, TX: 0x0005976415
RX: 0x0002BF8BF7, TX: 0x0005398C15
RX: 0x0003133D19, TX: 0x00058D3E15
RX: 0x0002F23CEB, TX: 0x00056C3C15
RX: 0x000327646E, TX: 0x0005A16415
RX: 0x0002C933D1, TX: 0x0005433415
RX: 0x00031E5CAF, TX: 0x0005985C15
RX: 0x0002BEEF1D, TX: 0x000538F015
RX: 0x0003135A25, TX: 0x00058D5A15
RX: 0x0002B6A5A2, TX: 0x000530A615
RX: 0x00030AA2A8, TX: 0x000584A215
...

The transmit time stamp may be due to something like the antenna Tx delay value. The internal time that the transmit starts will end in 9 0’s but the reported timestamp is then corrected for the delay resulting in a value that ends in a non-zero value.

Yes, I have not figured out exactly where this delay comes in.
Anyway TX time stamp is fully predictable from lower 9 bits of RX timestamp so it really doesn’t matter if its zeroed or not.