Rx readstream stops after first Tx writestream (EDIT: or just how to simultaneously Tx and Rx with lime mini)

Hello,

I have an application that worked with two HackRFs (one Tx and another RX) but when I converted it for my new lime mini I’ve got a problem.

I want to continuously transmit and receive. My first process grabs data from the receiver continuously using a while loop:

while True:
        sr = sdr.readStream(rxStream, [buff], len(buff), timeoutUs=int(5e5))
        print("Number rx samples {}".format(sr.ret)) #num samples or error code

The above bit of code starts before the transmitter write process. Initially the receiver returns the correct number of samples. However, my other Tx process which starts a bit later acquires data from a queue so that the Tx can transmit it:

samps_chan = queueForTx.get()
status = sdr.writeStream(tx_stream, [samps_chan], samps_chan.size, timeoutUs=int(5e5))

As soon as the transmitter does a writeStream though the receiver readStream starts returning -1. The writeStream is returning a valid number via status though.

I don’t have much experience with SoapySDR and never with a full duplex bit of kit. Are there any rules about Rx and Tx with lime mini (most hits on the forum didn’t help me)?

I should also mention I can run measureDelay.py example successfully but that doesn’t continuously stream on the Tx.

Thanks

I’ve tried to go back to basics on this problem and reverted to the simple MeasureDelay.py example provided at:

https://github.com/pothosware/SoapySDR/blob/master/python/apps/MeasureDelay.py

The default examples works and I even plotted the received signal to check it had the transmitted sinc in it. I then went back to the original copy of MeasureDelay.py but changed the “break” on line 120 that triggers when status.ret from readstream is <=0. I also print out the status.ret value i.e. line 120 and 121 are now:

else:
    pass # Was break
 print(status.ret)

This showed the receiver stopped receiving after a total of 10,000 samples had been transmitted. This led me to re-examine the documentation for activateStream(…) which by chance in MeasureDelay has a parameter called num_rx_samples which has value of 10,000. Therefore I changed the call to activate stream on line 101 to not specify any timing info:

sdr.activateStream(rx_stream) # Was sdr.activateStream(rx_stream, rx_flags, receive_time, num_rx_samps)

This let the receiver continually stream data in - so all good up to this point.

I’ve now gone back to trying to get a multiprocessing version going but keeping it as simple as possible. I stripped out all the tx code/process and just tried to receive continuously. The readStream fell over on its own at this stage - so the Tx chain has nothing to do with this. It seems to just be having a separate process to call readStream(…) that causes it to fall over?

To check this I called the code that was in the target function for the multiprocess logic directly (i.e. no multiprocessing going on). This code was as follows:

    #create a re-usable buffer for rx samples
    buff = np.array([0]*2040, 
                    np.complex64)

    # Receive samples, find pilot tone, put in neural network queue.
    totalRead = 0;
    while True:
        print("The buffer length is {}".format(len(buff)))
        sr = self.sdr.readStream(self.rxStream, [buff], len(buff), timeoutUs=int(5e5))
        print("Number rx samples {}".format(sr.ret)) #num samples or error code
            
        if sr.ret == -1:
            break
            
        totalRead += sr.ret

    print("Total samples read {}".format(totalRead))

This worked fine! So I am now completely lost why I can’t put the above code in a function and kick it off as a separate process - is there something in readStream that is not thread safe?

My code to call the above process wrapped in a function called recvSamples was:

# Create a RX process.
rxID = 1;
rxProcess= multiprocessing.Process(name='rx',
                                   target=trans.recvSamples,
                                   args=(rxID,))

# Start the TX and RX processes.
rxProcess.start()

How are we meant to simultaneously transmit and receive data from the limeSDR mini if we can’t call readStream and writeStream in separate threads/processes?

Any help/pointers appreciated as I’m running out of ideas here.
Thanks

I’m getting a bit desperate trying to think of ideas to solve this. In case it helps I’ve got the smallest code to reproduce my problem. First file is called run.py and just sets up the process:

#!/usr/bin/env python
"""
Example code to run the receiver in it's own process.
"""

import logging
import multiprocessing
import numpy as np
import SoapySDR
import transX


########################## SETUP ######################################

# Create a queue that the transmitter will take data from.
rxQueue = multiprocessing.Queue()

# RF param.
fc = 433e6
rate = 1e6

# Create the SDR device that resides on this compute platform.
sdrArgs=dict(driver="lime")
sdr = SoapySDR.Device(sdrArgs)

# Create a transmitter.
trans = transX.TransX(fc,
                      rate)

if __name__ == '__main__':

    # Set logging up.
    multiprocessing.log_to_stderr(logging.DEBUG)
    logger = multiprocessing.get_logger()
    logger.setLevel(logging.INFO)

    # Create a RX process.
    rxID = 1;
    rxProcess= multiprocessing.Process(name='rx',
                                       target=trans.recvSamples)
    
    # Start the TX and RX processes.
    rxProcess.start()

# Join receiver process in.
rxProcess.join()

The other file is the module transX.py that sets up the Lime mini and has the target function recvSamples() used above:

''' Class to transmit and receive signals from a limeSDR.'''

import multiprocessing
import numpy as np

import SoapySDR
from SoapySDR import SOAPY_SDR_TX, SOAPY_SDR_RX, SOAPY_SDR_CF32
import time


class TransX:

    def __init__(self, 
                 fc,
                 rate):

        # Basic setup for parameters.
        self.rate=rate
        self.freq=fc
        self.rx_chan=0
        self.rx_gain=30

        # Now create the sdr object.
        # Get a handle on the limeSDR mini.
        self.sdr = SoapySDR.Device(dict(driver="lime"))

        # Set sample rates.
        self.sdr.setSampleRate(SOAPY_SDR_RX, 0, self.rate)

        # Set frequencies.
        self.sdr.setFrequency(SOAPY_SDR_RX, self.rx_chan, self.freq)

        # Set gains.
        self.sdr.setGain(SOAPY_SDR_RX, self.rx_chan, 'LNA', self.rx_gain)

        # Setup Rx stream (complex floats)
        print("Create Rx stream.")
        self.rxStream = self.sdr.setupStream(SOAPY_SDR_RX, SOAPY_SDR_CF32, [self.rx_chan])

        # Sleep before activating streams.
        time.sleep(1)

        # Activate RX stream.
        self.sdr.activateStream(self.rxStream)


    def recvSamples(self):
        ''' Take IQ data from queue and then transmit.'''

        # Get this processors name.
        name = multiprocessing.current_process().name
        """worker function"""
        print('Process: ' + name + ' starting')

        #create a re-usable buffer for rx samples
        buff = np.array([0]*2040, 
                        np.complex64)

        # Receive samples, find pilot tone, put in neural network queue.
        totalRead = 0;
        while True:
        
            
            # Read from the stream (is this thread/multiprocess safe?).
            sr = self.sdr.readStream(self.rxStream, [buff], len(buff), timeoutUs=int(5e5))
            print("Number rx samples {}".format(sr.ret)) #num samples or error code

            if sr.ret == -1:
                break
                
            totalRead += sr.ret


        print("Total samples read {}".format(totalRead))

        return

The output I get when doing $> python run.py is:

[INFO] Make connection: 'LimeSDR Mini [USB 3.0] 1D53B04DD6611C'
[INFO] Reference clock 40.00 MHz
[INFO] Device name: LimeSDR-Mini
[INFO] Reference: 40 MHz
[INFO] LMS7002M calibration values caching Disable
[INFO] Selected RX path: LNAW
Create Rx stream.
[INFO] Rx calibration finished
[INFO/rx] child process calling self.run()
Process: rx starting
Number rx samples 2040
Number rx samples -1
Total samples read 2040
[INFO/rx] process shutting down
[INFO/rx] process exiting with exitcode 0
[INFO/MainProcess] process shutting down

Sometimes the total number of samples read is 0 though. If I call the function recvSamples(self) not in a process though i.e. in run.py if I invoke trans.recvSamples() then the readStream streams away very happily.

Any suggestions as to why this doesn’t work and what I can do to fix this would be most appreciated.
Thanks

I’ve solved it but don’t understand why - if I use threading rather than multiprocessing library the above work. Must be something to do with sdr object being shared over CPU versus via threading IO?

With the LimeUSB, and probably with the Lime Mini, changing the sampling rate while the stream is open causes issues. Also, changing the sampling rate in one direction changes the sampling rate in the other direction. So it’s best to first configure both RX and TX, then start the streams. Maybe that can explain why you get -1 after you start transmitting.