Minimising round-trip latency

I am currently trying to use the LimeSDR as an RFID reader. A quick summary of the relevant parts of RFID:

The reader tries to talk to an RFID tag by sending out a continuous wave (constant amplitude sine wave, around 915 MHz for my application), and then modulating its amplitude. The tag is powered by the reader’s transmitted waves, and talks back to the reader by changing how it reflects the continuous wave (backscattering).

In part of the protocol, the reader begins by sending a QUERY command. The tag then responds with an RN16 (a random 16 bit number). Upon receiving a valid RN16, the reader then needs to send another command to the tag within 500 us of the tag sending the last bit of its RN16.

So essentially, the tricky bit here in terms of latency is that the LimeSDR reader needs to see the RN16 at its RX port, get it through to the computer, process it, send the response to the TX, and the response needs to arrive at the tag, all within 500 us. I’m defining ‘latency’ here to mean the time between the last bit of the RN16 being sent from the tag, and the first bit of the reader’s response to the RN16 reaching the tag. Currently, the latency I’m achieving (measured using the RX on the LimeSDR) is around 2.5ms.

I’m using this RFID library on GNU Radio. I know that the issue is not with GNU Radio or this library, because others have successfully used this library in GNU Radio with Ettus devices.

The next layers of the software stack are gr-osmosdr, then SoapySDR, then finally the SoapyLMS7 API for the lime drivers. I’ve checked in both gr-osmosdr and SoapySDR, and to the best of my knowledge there are no buffers in either layer which could be causing the latency, so I feel like they are not the issue.

Within the SoapyLMS7 API, I have already forced the performanceLatency parameter (e.g. here) to be zero, which did improve things considerably, but only to the extent described above (note that even though it says there that the parameter should be 1 to minimise latency, this is in fact correct, and is contradictory with other parts of the LimeSuite code; I opened a github issue about this here ).

I found this thread which seems like it talks about some relevant stuff. The particular comment I linked to especially I think could be of interest, although I admit that a lot of the discussion in the thread goes a bit over my head. In the Lime streaming source there is this line which seems to be setting a streaming buffer size. The previous line defines ‘packetsToBatch’, which is related to ‘txbatchSize’ (or ‘rxBatchSize’), which is the parameter which is set by the latency parameter discussed above, and it’s already as small as it can go. The only other parameter that then controls the bufferSize is the FPGA_DataPacket size, which is defined here. I’m kind of scared to mess with this, as the buffer sizes here are defined in numbers rather than as constants, which could suggest that they’re not meant to be changed. Fiddling around with how packets are sent to the FPGA sounds like a good way to brick the board (without knowing what I’m doing at least).

Does anyone have any suggestions of what I can do to bring the latency down any more? I’ve tried a few different USB3 cables with no effect. I also installed a new USB3.1-PCIe card in my computer to see if that was the culprit, but no effect there either.

A couple of interesting things to note:

  1. My current sample rate is 1 MS/s for the DAC, and 2MS/s for the ADC. If I try and increase these by a factor of 2 the latency does not change. If I increase these by a factor of 4 however, the application quickly seg faults. This could suggest that somewhere a buffer is being written to an invalid address (i.e. filling up more quickly than it is being emptied), or something is trying to grab samples out of an empty buffer (i.e. emptying more quickly than it is filling up)?

  2. I have made a few other changes to the SoapyLMS7 source code in order to fix some of the other issues I have been having. These were: disabling the digital DC corrector in both TX and RX; and forcing the baseband frequency to be zero in all cases. I doubt these are relevant but have mentioned them just in case.

I realise that in this thread a while back I seemed to have solved the issue. However, for some reason I can no longer replicate those results, and I have no idea why.

I have the same latency problem in my modem project : github issue link .
in this thread you said you could get 500us latency !!? is that correct or it is a typing mistake ?

So at the end of my post above I do mention that I managed to get that at one point. However, some time in the intervening weeks since then, I have lost the ability to replicate those results, even though I went back to (what I believe is) that exact same configuration.

Thanks for the link though, I had looked at your issue before when researching this issue but got distracted by other problems I was having with the Lime and had forgotten I had seen it. I had actually just independently found out (again) about the config.bufferLength parameter and had come back to this thread to post about it when I saw your post.

The next thing I will try with my Lime is to reduce that buffer length and see if that improves things. I’m confident that gnuradio buffers are not the issue as it’s possible to get an RFID reader working with gnuradio, many people have done this with the Ettus hardware. Unfortunately (from the perspective of my project) I’m now away from my Lime for the next few weeks so I won’t have a chance to try it out for a while.

With regards to your particular problem, I have two suggestions on what might reduce the latency:

  1. Try putting the following line here:

    config.performanceLatency = 0.0

The comment there which says that 1 is lowest latency is backwards, I’m fairly certain. I opened a github issue about it (my post at the start of this thread has a link to it).

  1. Make sure you are running gnuradio in a way which forces your computer to prioritise running gnuradio and not get distracted by other tasks in the background. The way you can do this is to use realtime execution. Use the following command:

    sudo GR_SCHEDULER=STS nice -n -20 python ./

replacing ‘’ with the name of the python code for your gnuradio flowgraph. I have to admit I haven’t really looked into how this command works, it was recommended with the RFID gnuradio library that I am using.

The minimum transfer size in LimeSuite is a single FPGA packet. A single FPGA packet contains 1020 samples. So at 1MS/s it translates to ~1ms just to fill the packet buffer in FPGA (before it is sent via USB). If you want to reduce the packet size, you need to edit FPGA code. Increasing sample rate should help with latency.

How difficult would editing the FPGA code to reduce the packet size be? Are there any resources on this/has anyone tried to reduce the packet size for latency reasons already? I unfortunately have no prior experience with FPGAs…

Yes , i have tried that , when config.performanceLatency is 0 it have lowest latency .

@ashtum I yesterday managed to dramatically reduce my latency. Aside from what I mentioned above, I tried switching to SoapyUHD rather than gr-Osmosdr to use for my gnuradio blocks, and this gave a big improvement. I’m now able to achieve somewhere between 450-650us round-trip latency with my application, which allows me to have a ~80% success rate when trying to read tags (the protocol specificies a maximum of 500us). Earlier I was getting ~1ms round trip latency at the same sampling rate.

I achieved the above latency by using an ADC sampling rate of 6.7MS/s and a DAC rate of 3.35MS/s.

I hope this helps.

Thank you @DasSidG , Did you achieved that with just changing gnu-radio block ? without any change in driver ?

So I still have all the changes I mentioned in my previous posts on this thread. But yes, since then all I changed was the gnuradio block, without editing the drivers further.

I did not make any changes to the FPGA.

you changed only config.performanceLatency to 0 ?

Yes (I made some other driver changes but they weren’t relevant to the latency). I also increased the sample rates from when I first posted in December (which I gave in my post above today).

I also run the program using the realtime execution command I gave above.

Hello, I installed the gr-sdr from SoapyUHD, but it seams not working correctly for me. the flowgraph works but nothing shows on my spectrum analyser…

I just added the Device address parameter: {‘driver’ : ‘lime’}

Should I parameter the rest in order to have a block working?



Sorry, I’m a bit busy atm and don’t have access to my SDR stuff (will have access again in a couple of weeks time). I can’t remember exactly, but I think the device parameter was ‘driver=lime, soapy=0’.

I assume you’ve followed the instructions here?

I’m sorry I can’t really offer anymore help right now, I have final exams very soon. When I get back to my SDR stuff in a few weeks I can check exactly what I did there.

Thank you for your answer.

Exactly, I followed the instruction you posted, but the result was not attended… When I start the flowgraph, I can see that my Lime is detected and configured but noting on the spectrum… Anyway i will dig a little bit more and see If I find something else.

I am still surprise that this problem does not appear to more people…

Good luck for your exams!

I have also tried to get RFID library work with LimesdrUSB and mini. I have used B200 in past and with certain parameters I can get about 80-90% success rate with EPC reading.

I have two problems (at least) with Limesdr

  1. DC problem. The signal seems to be nice smooth with B200, but there is strange dc offset changes with limesdr. Any idea how to fix dc offset problem?
  2. Latency. I’m not sure how many us I have latency, but it is probably the issue. I can see response to RN16 but I’m receiving lot of “popping from TX, samples popped 0/1020” warnings. I have also tried to change config.performanceLatency values, but I have not seen any impact yet. How you have been able to obtain 450-650us latency?

Hi Matti,

Regarding your DC problem, that sounds familiar - have you seen these threads:

Regarding the latency, everything I did to reduce the latency is described above in this thread. The three things that fixed the latency issue for me were changing config.PerformanceLatency parameter, switching to using the SoapyUHD GNU Radio block rather than grOsmoSDR block, and increasing the sampling rate as high as I could (before I started getting segmentation faults).

Hi samv,

Apologies for the delay in getting back to you. This is (part of) the code I use to connect to the LimeSDR in GNU Radio:

from gnuradio import eng_notation
from gnuradio import gr
from gnuradio import blocks
from gnuradio import filter
from gnuradio.eng_option import eng_option 
from gnuradio.filter import firdes
from optparse import OptionParser
from gnuradio import analog
from gnuradio import digital
from gnuradio import uhd
import sip
import sys
import time
import rfid

import time    

self.u_source = uhd.usrp_source(
    ",".join(("", "driver=lime,soapy=0")),
self.u_sink = uhd.usrp_sink(
    ",".join(("", "driver=lime,soapy=0")),
self.u_source.set_center_freq(self.freq, 0)
self.u_source.set_gain(self.rx_gain, 0) #spread between TIA, PGA, LNA
self.u_source.set_antenna("LNAL", 0)
self.u_sink.set_center_freq(self.freq, 0)
self.u_sink.set_antenna("BAND1", 0)

Everything else in my script is not relevant to connecting to the LimeSDR, and most of the imports I included there are probably not relevant to actually connecting to the LimeSDR (but I included all of them just in case)

Great thank you I will try you code in my script.
I will also try the new block made by myriadrf : gr-limesdr. Normally the latency should not be here in this block.
I will update the status with the new block.

Hi DasSidG,

I was finally able to solve dc offset and latency issue. Thank you for the help. I modified config.performanceLatency to 0.0 and added “lms7Device->WriteParam(LMS7param(DC_BYP_RXTSP),1, channel);” to Streaming.cpp. I have now success rate for EPC read max. 80%. Do you think that this can be somehow improved?

With some parameters, I can see strange spikes and at beginning of signal I can see something strange.

Hi Matti,

That’s great news. A maximum read rate of 80% is similar to what I have achieved. Based on experiments I have done I believe that this read rate is due to latency; there are a certain number of ACKs which are sent too long after the RN16 still, which is responsible for pretty much all of the missed EPCs I believe. I haven’t yet figured out a way to reduce the latency further; one avenue which might be promising (as suggested by samv above) might be to use the new gr-limesdr block rather than the UHD block with SoapyUHD, as this might reduce the latency introduced by SoapyUHD and SoapySDR.

With regards to the strange waveforms at the beginning of the signal; I believe I also came across this. I believe that this is due to the calibration procedure carried out by the LimeSDR. This actually caused me some issues as it caused the ACKs and RN16s to become desynchronised occasionally (so that the ACK was being sent for the wrong RN16). To fix this I added a delay in the SoapyLMS7 drivers, after the calibration procedure but before the stream was enabled. So in ‘Streaming.cpp’, in the activateStream function, I added:


straight before

if (not icstream->enabled)
    for(auto i : streamID)
        int status = _conn->ControlStream(i, true);
        if(status != 0) return SOAPY_SDR_STREAM_ERROR;
    icstream->enabled = true;

Note you will also need

#include <fstream>
#include <chrono>
#include <thread> 

‘fstream’ may not be necessary for this actually, I might have added it for something else I was trying earlier, but I can’t remember.

This wont stop the LimeSDR actually transmitting those weird waveforms, but it will prevent those from being present in the samples received by GNU Radio at the beginning.

I hope that helps.