Frequency hopping transmit [FIXED]

I’m wondering what the best way to do frequency hopping transmit, as well as how fast can I expect to go.

I want to transmit 1MHz bandwidth signals between 2400MHz and 2500MHz.

I’m using Soapy, and calling setFrequency is pretty slow, 40 to 60ms.

Using setFrequencyComponent instead is a lot fast. I first set the RF component to 2450MHz, then I set the BB component to shift where I want. It takes 2.2ms with an old version of Pothos, while with the latest (2018-11-04) it takes 4.4ms (I opened https://github.com/pothosware/SoapySDR/issues/192 but I’m not sure it’s the right place).

  • Any idea why it’s slower now?
  • Is that the best way to proceed? Or can I use some lower level things to shift frequency faster? I’ve read LimeSDR Signal Generator by @ricardas but I’m not sure it’s different from what I’m doing.
  • How fast can I expect to go?

EDIT: just after posting I noticed Basic Frequency Hopping In GNURadio Not exactly the same, but related question for the how fast.

The speed regression was most likely caused by LimeSuite change: myriadrf/LimeSuite@c02f435#diff-d2304d4575d79f2082317421ac086e5bL888

quickest way to change would be by using NCO to upconvert or downconvert your frequency by changing SPI register 0x0240[4:1] bits, which allows up to 16 possible jump frequencies. Of course with NCO the available jump range would depend on your sampling rate.
LimeSDR-USB shows that SPI register write/read roundtrip is around 151.794 us, don’t know if there would be any additional delays at hardware level until it takes effect in RF.

You can also try passing '‘cacheCalibrations=1’ to Soapy (if that is possible from Pothos):

this should enable register cache and reduce number of reads/writes when changing settings.

If your target tuning speed is “at least one Bluetooth session at a time,” I think it’s going to be a tough road with LimeSDR unless you write a lot of custom gateware (and maybe sync 2 of them, depending on which Lime device you’re using) I don’t think a PC being involved in orchestrating the phy is going to be possible.

But I want nothing more than for someone to explain why this belief is wrong.

Thanks @IgnasJ , I can indeed enble caching by adding:
SoapySDRKwargs_set(&args, "cacheCalibrations", "1");
>[INFO] LMS7002M calibration values caching Enable
while by default I get
>[INFO] LMS7002M calibration values caching Disable

When I use Pothos 2018.04.08 I don’t see any difference (always 2.2ms), but when I use Pothos 2018.04.26, I go straight from 4.4ms to 2.2ms. Does that mean caching was always enabled in older versions, or it has nothing to do with it?

Thanks @ricardas. If the speed regression is indeed caused by that LimeSuite change, can it be rolled back? Or does that mean that there used to be a bug in a previous versions, and fixing the bug just inevitably makes the frequency shift slower?

Am i right in thinking that setFrequencyComponent with RF will set the radio while with BB it will change the NCO? If yes then what’s the difference with using the SPI registers?

Do you have more info (eg code sample) on how to use those registers exactly? I guess those are not accessible through Soapy, but only directly through the Lime API.

So I could put up to 16 frequency offsets, and then just switch between them. Now why would that be faster than just changing the value of one of those 16 registers? Is it because there’s some caching involved, and setting one of those 16 registers is time consuming, but then switching is really fast?

Don’t know the reason for the change, but it does not look like a bug, just more configuration done, meaning more SPI writes. If caching gives you the same timing you don’t have to worry about this.

Correct, the only difference is that using API to set the values is doing extra work and some of it might be redundant. By manipulating SPI registers you can avoid that and do minimal ammount of configuration necessary.

Yes you can alternatively just keep reconfiguring one of the entries, it should be just as fast as switching. The difference is that to switch you need to change only 1 register, but to reconfigure the frequency you need to modify at least 2 registers (which would need to be calculated based on sampling frequency and reference clock

Thanks a lot for the detailed answer. I’ll look into it.

Hi Ricardas,

I tried using the LimeSuite C API instead of the Soapy API. I call first LMS_SetNCOFrequency to set the 16 frequencies, then I call LMS_SetNCOIndex to use the frequency at the specified index. Just calling LMS_SetNCOIndex seems to take 2ms, which is similar to what I had before.

I then tried calling straight the 2 SPI_write and it is indeed way faster, aroung 350us for the 2 calls, knowing that GetReferenceClk_TSP takes around 800us. There must be other things to do than those 2 SPI_write though, as it doesn’t seem to be working.

Also I’m getting some syntax error: 'constant' in lms7002m.h each time I try to #include "lime/lms7_device.h". I’m on Windows 10, Visual Studio 2017. If I only create a minimalist file, it does work. There must be some conflicts with other files.

The thing is the LimeSuite.h API is doing a lot of redundant work, to simplify usage and make sure everything is configured for used functionality (for example when you set nco frequency, it also powers it up and sets either upconvert or downconvert SetNCOFreq. When you are writing directly to the registers or using other lower level functions, you can use your knowledge of when and what parameters you intend to change to eliminate the redundant operation, but then it’s also up to you to set up all required configuration dependencies.
I’d say the easiest way would be to setup the initial working conditions using API calls, and then during runtime only modify the desired register to tweak values. You can use LMS_WriteLMSReg function from the LimeSuite.h to write SPI registers. Don’t know why you’re including lms7_device.h and what other headers you’re including, so it might some headers clash, or your project compiler flags.

My mistake. It is indeed working well, thanks a lot!

For anyone interested I do something like this

// Use high level function to sets things right.
device->SetNCOFreq(tx, channel, index, freq);
...

lime::LMS7002M* lms = device->GetLMS();
uint16_t addr = tx ? 0x0240 : 0x0440;

// Use low level functions to directly set the NCO.
uint32_t fcw = uint32_t((otherFreq/ refClk_Hz) * 4294967296);
lms->SPI_write(addr + 2 + index * 2, (fcw>> 16)); // NCO MSB part.
lms->SPI_write(addr + 3 + index * 2, fcw); //NCO LSB part.

This allows skipping a lot of costly calls that in my case, going from 4.4ms to 0.350ms. That’s an awesome improvement!

I’m only using channel 0, but I would probably need to call something like lms->Modify_SPI_Reg_bits(LMS7param(MAC), channel); if I wanted to go from one channel to another.

You can use LMS7002M::SPI_write_batch which would allow to make those two writes faster.

Correct. There is also possibility to write to both channels at the same time ( by setting channel to 3, but this is valid only for writing, trying to read would return random values).

I was even able to implement this through Soapy. I can easily access the 2 lms->SPI_write through SoapySDRDevice_writeRegister. The only problem was getting the refClk_Hz as I couldn’t access it straight through Soapy. As I wasn’t sure about its value, for now I copied the code of the method to get it, which required a few SoapySDRDevice_readRegister, as long as there’s no cache.

I was thinking that Modify_SPI_Reg_bits() uses the register cache for reads but not for writes. Since we know the value of a register, there is no good reason to also write a register again if its already the same value… At least in most cases. Might be a worthwhile change in general and I think it would also resolve this issue.

1 Like