RX timestamp consistency

Hi,
I’m developing TDoA (OWR) system using with DW1000.
I’m doing a basic operation test to do this.
The test is like below.
Anchor#0 : Delayed TX in every 1000ms
Anchor#1 : Immediate RX in whole time.

Anchor#0 sends a packet in every 1000ms(1sec) with DW1000’s delayed TX function like below.
Source code is like below.


:
:
u64TxTime = get_tx_timestamp_u64() + convertmicrosectodevicetimeu((double)(TEST_DW1000_DELAY_TIME_US));
u64TxTime >>= 8; // 32 bit hi
dwt_setdelayedtrxtime(u64TxTime);

    if(dwt_starttx(DWT_START_TX_DELAYED) == DWT_ERROR)         //transmit the frame
    {
        DBG_MSG_L0("(dwt_starttx() == DWT_ERROR)\n");
    }

    u32BeginMs      =       portGetTickCount();
    QUEUE_RECEIVE( g_hDwEventQueue, &u32Event, portMAX_DELAY);        
    u32EndMs        =       portGetTickCount();

    u64TxTimeStamp      =       get_tx_timestamp_u64();        

    dw_event = instance_getevent(11); //get and clear this event

    DBG_MSG_L0("\n---------------------------------------------------\n");
    if(dw_event->type == DWT_SIG_TX_DONE)
    {       
        DBG_MSG_L0("%s#%d, TX complete![#%d]\n", (inst->mode == TAG) ? "Tag" : "Anchor", u32AncAddr, ++u32Count);

        DBG_MSG_L0("TS : tx=%lfms\n", u64TxTimeStamp*DWT_TIME_UNITS*1000);
        DBG_MSG_L0("RngNum=%d\n", inst->rangeNum);
    }
    DBG_MSG_L0("elapsed = %dms\n", u32EndMs -u32BeginMs);    
    DBG_MSG_L0("---------------------------------------------------\n");

:
:


It works well and the result display screen via serial terminal is like below.


:
:

Anchor#0, TX complete![#4]
TS : tx=9760608.621795us
RngNum=3
elapsed = 1005ms


Anchor#0, TX complete![#5]
TS : tx=10760608.621795us
RngNum=3
elapsed = 1005ms


Anchor#0, TX complete![#6]
TS : tx=11760608.621795us
RngNum=3
elapsed = 1006ms


Anchor#0, TX complete![#7]
TS : tx=12760608.621795us
RngNum=3
elapsed = 1005ms


Anchor#0, TX complete![#8]
TS : tx=13760608.621795us
RngNum=3
elapsed = 1004ms


Anchor#0, TX complete![#9]
TS : tx=14760608.621795us
RngNum=3
elapsed = 1005ms


Anchor#0, TX complete![#10]
TS : tx=15760608.621795us
RngNum=3
elapsed = 1005ms

:
:


As you can see, TX timestamp has very consistent value.
It correctly sends a packet in every 1000ms without any difference like follow.
:
:
TS : tx=9760608.621795us
TS : tx=10760608.621795us
TS : tx=11760608.621795us
TS : tx=12760608.621795us
TS : tx=13760608.621795us
TS : tx=14760608.621795us
TS : tx=15760608.621795us
:
:

Anchor#1 receives this packet and display RX time stamp in all the time.
Source code is like below.


:
:

dwt_enableframefilter(DWT_FF_NOTYPE_EN); //allow data, ack frames;
// First time anchor listens we don't do a delayed RX
dwt_setrxaftertxdelay(0);
dwt_setrxtimeout(0);
dwt_setpreambledetecttimeout(0);

do
{
    dwt_rxenable(DWT_START_RX_IMMEDIATE) ;  // turn RX on, without delay
    
    u32BeginMs      =       portGetTickCount();
    QUEUE_RECEIVE( g_hDwEventQueue, &u32Event, portMAX_DELAY);
    u32EndMs        =       portGetTickCount();

    dw_event = instance_getevent(15); //get and clear this event

// su64RxTimeStamp = get_rx_timestamp_u64();
su64RxTimeStamp = dw_event->timeStamp;

    u8SavedEventType        =       dw_event->type;

:
:
DBG_MSG_L0("\n========================================\n");
if(u8SavedEventType == DWT_SIG_RX_OKAY)
{
DBG_MSG_L0("%s#%d, RX complete![#%d]\n", (u32InstanceType == TAG) ? “Tag” : “Anchor”, u32AncAddr, ++u32Count);
DBG_MSG_L0(“TxAnchor=%d, RngNum=%d, TxAntDly=%d\n”, u32TxAncAddr, u32TxRngNum, u32TxAntDly);

            sdbInterval         =           (su64RxTimeStamp - su64PreviousRxTimeStamp)*DWT_TIME_UNITS*1e6 - _TEST_DW1000_DELAY_TIME_US_;
            DBG_MSG_L0("TS : rx=%lfms, interval=%lfus, diff intv.=%lfus\n", su64RxTimeStamp*DWT_TIME_UNITS*1e3, sdbInterval, (sdbPreviousInterval - sdbInterval));
            DBG_MSG_L0("TA0A%d=%lfus\n", u32AncAddr, g_dbTimeDistanceFromAnchor0[u32AncAddr]);
        }
        else if(u8SavedEventType == DWT_SIG_RX_TIMEOUT)
        {
            DBG_MSG_L0("DWT_SIG_RX_TIMEOUT\n");
        }
        DBG_MSG_L0("elapsed = %dms\n", u32EndMs -u32BeginMs);    
        DBG_MSG_L0("========================================\n");  

:
:


It works well and the result display screen via serial terminal is like below.


:
:

Anchor#1, RX complete![#556]
TxAnchor=0, RngNum=3, TxAntDly=0
TS : rx=12061.936409ms, interval=1.426564us, diff intv.=0.000610us
TA0A1=0.008875us
elapsed = 991ms

========================================
Anchor#1, RX complete![#557]
TxAnchor=0, RngNum=3, TxAntDly=0
TS : rx=13061.937838ms, interval=1.429522us, diff intv.=-0.002958us
TA0A1=0.008875us
elapsed = 992ms

========================================
Anchor#1, RX complete![#558]
TxAnchor=0, RngNum=3, TxAntDly=0
TS : rx=14061.939270ms, interval=1.431290us, diff intv.=-0.001768us
TA0A1=0.008875us
elapsed = 991ms

========================================
Anchor#1, RX complete![#559]
TxAnchor=0, RngNum=3, TxAntDly=0
TS : rx=15061.940704ms, interval=1.434138us, diff intv.=-0.002848us
TA0A1=0.008875us
elapsed = 991ms

========================================
Anchor#1, RX complete![#560]
TxAnchor=0, RngNum=3, TxAntDly=0
TS : rx=16061.942142ms, interval=1.437988us, diff intv.=-0.003850us
TA0A1=0.008875us
elapsed = 991ms

========================================
Anchor#1, RX complete![#561]
TxAnchor=0, RngNum=3, TxAntDly=0
TS : rx=17061.943570ms, interval=1.428536us, diff intv.=0.009453us
TA0A1=0.008875us
elapsed = 991ms

:
:


As you can see, RX timestamp has inconsistent value dislike to TX timestamp.
It’s RX time(timestamp) is different in every time like follow.
:
:
TS : rx=12061.936409ms, interval=1.426564us, diff intv.=0.000610us
TS : rx=13061.937838ms, interval=1.429522us, diff intv.=-0.002958us
TS : rx=14061.939270ms, interval=1.431290us, diff intv.=-0.001768us
TS : rx=15061.940704ms, interval=1.434138us, diff intv.=-0.002848us
TS : rx=16061.942142ms, interval=1.437988us, diff intv.=-0.003850us
TS : rx=17061.943570ms, interval=1.428536us, diff intv.=0.009453us
:
:

I think that RX timestamp also has a consistent value in every time, if the TX time is consistent.
Can anyone answer or explain why the RX timestamp is different in this case ?

Thank you.

DongWook Kim.

Your difference is probably due to clock differences.

The transmitter is sending every 1000 us as timed by its internal clock.

The receiver is receiving every 1001.4 us as timed by its internal clock.

So if the clock on the receiver is running 1.4 us fast every second then that would explain the time difference.
1.4 us / 1 second = 1.4 parts per million. Well within the tolerances of the clocks used on the parts.

If you were to swap the units over so the receiver was transmitting and the transmitter was receiving then you should find the exact opposite error, your receive interval will be 999.9986 ms

If you measured it once each way and averaged the results you would get 1000 ms.

This clock error is why two way ranging is more accurate than single way ping and reply style ranging, if you measure the range in both directions then the clock errors cancel out.
Either that or you measure what the clock differences between the two ends are and compensate for it in software.

Hi AndyA,

Anchor#0 (tx) and Anchor#1(rx) position is static. Position is not changed and it is fixed.
The distance in between two anchors are about 2.99 meters. The flight time in between is 2.99 ÷ light speed = 0.008875 usec.
I think that rx time (timestamp) must not be changed all the time due the fixed position and fixed distance in between two anchors.
I am curious why rx time(except tx interval) is changed in every time.

Thank you.

DW Kim.

Your results were:
TS : rx=12061.936409ms, interval=1.426564us, diff intv.=0.000610us
TS : rx=13061.937838ms, interval=1.429522us, diff intv.=-0.002958us
TS : rx=14061.939270ms, interval=1.431290us, diff intv.=-0.001768us
TS : rx=15061.940704ms, interval=1.434138us, diff intv.=-0.002848us
TS : rx=16061.942142ms, interval=1.437988us, diff intv.=-0.003850us
TS : rx=17061.943570ms, interval=1.428536us, diff intv.=0.009453us

where:
rx is the internal clock time on the receiver
interval is the difference between the expected receive time and the actual receive time
diff intv is the amount that interval has changed.

So your interval has a constant error of around 1.43us.
As previously mentioned, this is due to clock differences between the transmitter and the receiver and is within the expected range. The transmitter and receiver think time is running at slightly different speeds and so you get this error. Short of replacing the crystal on each unit with an atomic clock there isn’t much you can do to eliminate this issue and so you just have to cope with it either by using a protocol that cancels it out or measuring it and subtracting it.

Ignoring that constant bias and only looking at the amount that the time period is bouncing around (the diff intv value) gives an interesting effect. Your seeing a variation of up to 9 ns in receive time after allowing for this bias, that is larger than I would expect since it represents several meters of range noise. The delayed transmit has an accuracy of 8 ns which would match the noise you are seeing but 1 second is an exact multiple of the clock speed and so that shouldn’t come into play.
Is there any chance that any of your code is truncating things to floats rather than doubles? I can’t see any obvious causes but not all of your definitions are included. e.g. if sdbInterval or sdbPreviousInterval were defined as floats then that would explain the this noise in the range values.

Where,
Interval represents the difference in between current rx time and previous rx time.
Diff.intv. represents the difference in between current interval and previous interval.

Variable of prefix sdb… represent that it is static double variable…

I totally agreed that two instances(anchors) must have different xtal oscillatior and thus there should be time diffrenece in between.

What I really curious thing is that tx time is very correct constant value. There is no change at all during tx operation.
However, rx time is slightly changed in every rx time.
Two anchor position is fixed. No move during the operation.
If so, rx time also should be constant during this operation.
Tx and rx time uses double variable in both.
I think that this has no effect to this.
But if you want, i can change this to float and try to test again…

Thank you.
DW Kim

The difference between current Rx time and previous Rx time is around 1000 ms. Your interval values are around 1.4 us and so are not that difference.
Interval is the difference Rx time and previous Rx time minus the expected interval. Which is the difference between rx time and expected rx time based on the previous rx time.

Tx time is always going to be consistent. Everything is timed on the same clock and your transmit period is an exact multiple of the clock period. That time is always going to be the same, the only way it wouldn’t be is if computers made rounding errors on integer maths.

Since everything is static and assuming line of sight and reasonable signal powers then interval should be almost constant and Diff Intv should be in the +/- 0.2 ns region.

I don’t know if it will have any effect but try
sdbInterval = (su64RxTimeStamp - su64PreviousRxTimeStamp - (uint64_t)63897600000) * DWT_TIME_UNITS * 1e6;
This subtracts the expected interval as an integer before converting to floating point and so should reduce the rounding errors. 63897600000 = 1 second in internal clock counts, most #defines or functions to generate this would use doubles so I’ve just hard coded it to make sure we get the exact integer. If this works you’ll probably want to tidy it up so you don’t have a hard coded number in there but it’s a quick way to test things.

Hi AndyA,

I think that the problem is not relevant to converting from integer to floating(actually, double point).
I have tried to do like below as you suggested.
sdbInterval = (su64RxTimeStamp - su64PreviousRxTimeStamp - (uint64_t)63897600000) * DWT_TIME_UNITS * 1e6;
But, the result was same.
And I also tried to change double point variables to float point variables as you suggested in before.
But, the result was same.

As I look at the result data, the interval time is variant from 2-3us(intially) to around 1.2us(finally) when the TX period is 1000ms.
And finally, it looks like to be saturated to a value of 1.2xxxusec after a several hundreds of seconds.
I think that Anchor#0(TX) also has a little XTAL variation, and Anchor#1(RX) also has a little XTAL variation as well in each. So, the interval value is changing forever in my idea…

I am trying to make a average interval value after a couple of TX and RX. And then, I would subtract it from RX timestamp as you mentioned in earlier.
But, as in my experiment, The interval value is always variant in a slightly in-regular(diif intv. is variant from 0.00xxns ~ 9ns).
So, I am not sure this way solves the problem.
If you have any good idea to overcome this problem, please let me know or suggest.

Actually, it seems to be a part of wireless synchronization algorithm in TDoA algorithm.
Am I right?

Thank you.
DW Kim.

It’s also worth mentioning that significant errors can occur if there are reflective objects nearby, particularly if combined with a barrier such as a person or wall. Although the DW algorithm takes timing from the first signal received, it’s not unknown in our experience for the first signal to sometimes be missed and then pick up a reflection adding maybe 1/2 to 1m to the distance.