Strange part-sinusoidal oscillation when using RX and TX together [RESOLVED]

Hi all,

I’m currently working on trying use the LimeSDR as a Gen2 Class 1 RFID reader, through GNU Radio, using this library. I’ve been having an issue with a strange sine wave on my RX which keeps getting partially cut-off. My hardware setup is a the Lime TX (BAND1) goes into a directional coupler; the output of this coupler (no attenuation from the TX) goes to an antenna with an RFID tag next to it; an attenuated part of the signal from the antenna then goes into the RX. A picture of the strange cut-off sine wave is below:

The above looks like a sawtooth, but it is in fact (I believe), a small section of a sinewave which gets cut-off, the amplitude set back low, and the sinewave starts up again - I will explain why I believe this shortly. Initially, I didn’t realise that this was the first part of a sinewave, and so I believed the problem was the same as in this thread , where someone else had a sawtooth-like oscillation, and the problem was resolved by bypassing the DC corrector in the LMS7002M. However, I tried the fixes mentioned in the thread and they didn’t seem to make any difference.

The above picture was taken with a DAC sampling rate of 1 MHz, and an ADC sampling rate of 2 MHz. Interestingly, I found that if I reduced the DAC and ADC sampling rates by the same factor, I could see a greater proportion of the sine-wave. The picture below shows the same code running with the DAC and ADC sampling rates reduced by a factor of 10:

(Note that the tall vertical lines are the modulation which I am actually trying to carry out using the RFID library); when the reader is not modulating, the waveform should just be a constant amplitude carrier wave i.e. a flat horizontal line)

It’s not a problem with the RFID library - i have a colleague using the same library successfully with a USRP device without any issues.

I did some tests trying to just do AM of a sinewave. I found an interesting result: When my DAC rate was equal to the modulating sinewave (yes I know that this will cause aliasing issues, which leads to me believe that the main problem is somehow related to aliasing) there was the same kind of oscillation as described above. However, if the ADC and DAC rates were increased by even 1%, the weird oscillation completely disappeared. Unfortunately for the RFID script, increasing the ADC and DAC rates did not alleviate the weird oscillation.

The problem doesn’t seem to be there in either the TX or RX channels individually; I viewed the TX waveform on a spectrum analyser, and it looked normal, without the strange oscillations. Similarly, I input a signal from an RFID test device into the RX (through an attenuator), with the TX disconnected (coupled into an antenna), and the RX picked up a reasonable signal, with no sign of any strange behaviour.

The above all leads me to believe that there is something strange going on in the interaction between the TX and RX; it feels like the sinusoid that is getting cut off is some kind of beat frequency resulting from some clocks which should be at the same frequency not quite being aligned, and they get cut off because their amplitude goes too high. That is probably complete rubbish though - I’m not yet intimately familiar with the inner workings of the LMS7002M.

Another strange signal that I often get is what is below:

This is again when attempting to run the RFID script, this time running at 100 kHz DAC and 200 kHz ADC. It doesn’t always appear though, and at higher sampling frequencies sometimes the waveform will start like this, and then abruptly transition into the waveform which the main problem that I am describing (i.e. the partially cut-off sine wave).

Anybody have any thoughts? This problem has been plaguing me for quite some time now and is slowly driving me insane…

A few details about my setup:

Using the latest versions of GNURadio, SoapySDR, and gr-osmosdr, all pulled from master (built from source)
Using the ‘stable’ branch of Limesuite

Note that I was having a latency issue, so in the SoapyLMS7 drivers in Limesuite, I forced the config.performanceLatency = 1 in the setupStream function in Streaming.cpp. Commenting out these changes didn’t affect the above.

I’m using a new USB3.1-PCIe card connected to a PCIexpress port on my computer (but switching between this and one of the normal USB3.0 ports on my computer makes no difference). I initially suspected a power issue, but tried with both an external power supply, and now with the new USB3.1-PCIe card, without either making a difference, so I am confident it is not a power issue.

The GNU Radio code I am running is the following:

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
import osmosdr
import sip
import sys
import time
import rfid

class reader_top_block(gr.top_block):

  def __init__(self):

rt = gr.enable_realtime_scheduling()

######## Variables #########
self.dac_rate = 1e6                 # DAC rate
self.adc_rate = 100e6/50            # ADC rate (2MS/s complex samples)
self.decim     = 5                    # Decimation (downsampling factor)
self.ampl     = 0.1                 # Output signal amplitude (signal power vary for different RFX900 cards)
self.freq     = 910e6                # Modulation frequency (can be set between 902-920)
self.rx_gain   = 0                   # RX Gain (gain at receiver)
self.tx_gain   = 0                    # RFX900 no Tx gain option

# Each FM0 symbol consists of ADC_RATE/BLF samples (2e6/40e3 = 50 samples)
# 10 samples per symbol after matched filtering and decimation
self.num_taps     = [1] * 25 # matched to half symbol period

######## File sinks for debugging (1 for each block) #########
self.file_sink_source         = blocks.file_sink(gr.sizeof_gr_complex*1, "../misc/data/source", False)
self.file_sink_matched_filter = blocks.file_sink(gr.sizeof_gr_complex*1, "../misc/data/matched_filter", False)
self.file_sink_gate           = blocks.file_sink(gr.sizeof_gr_complex*1, "../misc/data/gate", False)
self.file_sink_decoder        = blocks.file_sink(gr.sizeof_gr_complex*1, "../misc/data/decoder", False)
self.file_sink_reader         = blocks.file_sink(gr.sizeof_float*1,      "../misc/data/reader", False)

######## Blocks #########
self.matched_filter = filter.fir_filter_ccf(self.decim, self.num_taps)
self.gate            = rfid.gate(int(self.adc_rate/self.decim))
self.tag_decoder    = rfid.tag_decoder(int(self.adc_rate/self.decim))
self.reader          = rfid.reader(int(self.adc_rate/self.decim),int(self.dac_rate))
self.amp              = blocks.multiply_const_ff(self.ampl)
self.to_complex      = blocks.float_to_complex(1)

# Osmocom blocks
self.u_source = osmosdr.source(args="numchan=" + str(1) + " " + 'driver=lime,soapy=0'  )
self.u_source.set_center_freq(self.freq, 0)
self.u_source.set_freq_corr(0, 0)
#self.u_source.set_dc_offset_mode(0, 0)
self.u_source.set_iq_balance_mode(0, 0)
self.u_source.set_gain_mode(True, 0)
self.u_source.set_gain(self.rx_gain, 0)
self.u_source.set_if_gain(10, 0)
self.u_source.set_bb_gain(10, 0)
self.u_source.set_antenna("LNAL", 0)
self.u_source.set_bandwidth(0, 10000000)

self.u_sink =  osmosdr.sink(args="numchan=" + str(1) + " " + 'driver=lime,soapy=0'  )
self.u_sink.set_center_freq(self.freq, 0)
self.u_sink.set_freq_corr(0, 0)
self.u_sink.set_gain(self.tx_gain, 0)
self.u_sink.set_if_gain(10, 0)
self.u_sink.set_bb_gain(10, 0)
self.u_sink.set_antenna("BAND1", 0)
self.u_sink.set_bandwidth(0, 10000000)

######## Connections #########
self.connect(self.u_source, self.matched_filter)
self.connect(self.matched_filter, self.gate)
self.connect(self.gate, self.tag_decoder)
self.connect((self.tag_decoder,0), self.reader)
self.connect(self.reader, self.amp)
self.connect(self.amp, self.to_complex)

self.connect(self.to_complex, self.u_sink)

self.connect(self.u_source, self.file_sink_source)
self.connect(self.gate, self.file_sink_gate)
self.connect((self.tag_decoder,1), self.file_sink_decoder) # (Do not comment this line)
self.connect(self.reader, self.file_sink_reader)
self.connect(self.matched_filter, self.file_sink_matched_filter)

if __name__ == '__main__':

  main_block = reader_top_block()

    inp = raw_input("'Q' to quit \n")
    if (inp == "q" or inp == "Q"):


I’ve ordered a new USB3 cable which should be arriving on Monday.

Sorry, I didn’t get deep into your config as I don’t use gnuradio, but just in case - I saw topics here on forum about using Rx and Tx on same sample freq - in this case you should source both rx and tx from single PLL, otherwise some kind of troubles are to be expected due to two close but not equal freqs. Are you aware of it and doing this? I don’t have links at hand, but somewhere here it was mentioned several times.

Hi wowa,

Thanks for your reply, and sorry for the delay in mine; I’ve been away from my LimeSDR for the past week or so.

I believe what you are referring to could be solved by enabling TDD instead of FDD as described here for example. Thanks for the suggestion. I will try enabling TDD by fiddling with the SoapyLMS7 layer (I haven’t seen any indication that such an option would be available in the gnuradio, osmosdr, or soapysdr layers, but I’d love to be corrected if I’m wrong).

However, I’m not hugely hopeful for that solution as I feel like it doesn’t really explain what is going on with why the sinewave gets cut off; I would have thought that the sinewave would just continue. And why does reducing the sampling rate seem to allow more of the sinewave to appear before restarting?

The new USB3.0 cable I got didn’t make a difference unfortunately.

@joshblum you mentioned here that you were thinking of making TDD automatic when TX and RX are set to the same frequency. Scanning the commits on github, am I right in thinking that this hasn’t happened yet? In the meantime, to try and get TDD working in the SoapyLMS7 layer, am I right in thinking that I need to call writeRegister (defined in Settings.cpp) a few times with the valid registers? Something like:

writeRegister('RFIC0', 0x0020, 2); // LMS7_MAC
writeRegister('RFIC0', 0x011C, 0); // LMS7_PD_LOCH_T2RBUF
writeRegister('RFIC0', 0x0020, 1); // LMS7_MAC
writeRegister('RFIC0', 0x011C, 1); // LMS7_PD_VCO

The above doesn’t feel right as, for example, 0x011C is a register which contains many different parameters, and I don’t see how I’m able to specify which one (or more) of the bits specifically within that register I am targeting. Would you be able to guide what I should be writing instead? In the post I linked to you mentioned a writeSettings() function; however, I was not able to find this. The writeRegister I am referring to above is found here.

Alternatively, would it be better to use something like LMS_WriteParam with the relevant registers and values somewhere in say Settings.cpp or Streaming.cpp in Soapy, just after calibration somewhere?

In addition, where should I put the above code (once it’s correct) in order to ensure that it gets called when I run the gnuradio program (i.e. it needs to be in a place, somewhere after the initial calibration, in theSoapyLMS7 layer which will always get called)? Sorry if it seems like I’m asking for hand-holding; I was only introduced to SDR as a concept less than 2 months ago, so I’m still climbing the learning curve.

If I get the above working I will reply to the the other post about toggling FDD/TDD as others might find the same info useful in the future.

EDIT: Just found the writeSetting function (I had searched for writeSettings which is why I hadn’t found anything…). Reading through the function it doesn’t seem like there’s anything to do with doing the steps required for enabling TDD. I guess I’d need to add another else if statement with another key for something like ‘SET_TDD’ and then use something like:

rfic->Modify_SPI_Reg_bits(LMS7param(MAC, 2);
rfic->Modify_SPI_Reg_bits(LMS7param(PD_LOCH_T2RBUF, 0);
rfic->Modify_SPI_Reg_bits(LMS7param(MAC, 1);
rfic->Modify_SPI_Reg_bits(LMS7param(PD_VCO, 1);

Does that seem correct?



I’ve managed to resolve the above issues now. For anybody reading this in the future, there were two strange sources of oscillation which were causing me issues.

The first was the DC corrector, as has been discussed before in this thread. . I had tried a fix for this earlier, but I realise that I was putting the fix in the wrong place, and so it was getting negated. I will post more details of the fix that worked in that thread.

The second issue was that for some reason, for any frequency other than 915 MHz, the TX and RX BB frequencies were not being set to 0. but were instead being set to:

[INFO] SoapyLMS7::setFrequency(Rx, 0, BB, 2.44141e-06 MHz)
[INFO] SoapyLMS7::setFrequency(Tx, 0, BB, 2.44141e-06 MHz)

This is not an issue with the LimeSuite drivers, but something higher level (either in SoapySDR, Osmosdr, or GNURadio), which is for some reason calling setFrequency() with a strange value for BB. To fix this I just forced the frequency to always be 0 when trying to set the BB frequency, in the SoapyLMS7 drivers.

EDIT: it actually is caused by the LimeSuite drivers, see Josh’s replies below.

The default tuning algorithm tunes the LO and using any remainder in tuning error to tune the baseband NCO to achieve the centre frequency. In practice, this makes tuning below 30 MHz work. What you are seeing is basically a tuning error of 2 Hz just printed very oddly (2.44141e-06 MHz == 2.44Hz). It probably just happens that 915 MHz tunes perfectly without any error.

Of course, that seems silly for several Hz to even bother with this, but I didnt think any harm would come of that. It looks like its an issue for the dc removal filter. I made a branch ( that ignores the NCO tuning unless we are below 30 MHz if you are interested to try that.

Unfortunately the writeRegister just is a bit too low level compared to the setting of individual parameters. The idea would be to use readRegister and write register to read/modify/write. In intended to put something like “TDD”=“ON” into the tune args to implement this with the parameters like you mentioned – to have this as a manual hook. And then to do this automatically when the LO frequencies are the same. One of the delays is that we @IgnasJ are reimplementing SoapyLMS around the LMS7_Device API, which supports this mode so we dont have to duplicate parameter configurations.

With regards to the branch for the NCO tuning; I won’t have access to my Lime for another couple of weeks or so, so I will try it out then and let you know how that goes. Thanks for looking into it!

About the reading/writing registers for enabling TDD; I see now what you mean, that’s useful to know, thanks.