Post Go back to editing

pluto sdr pyadi-iio continous iq recording

Category: Software
Product Number: adalm pluto sdr
Software Version: rev C


I need to continously record IQ samples to file using pyadi-iio. My question is which is the recommended way to do it.

So far I succeeded for sdr.rx_buffer_size up to a limit, lets say 1M size.

However, how should I control this for a sample rate of lets say 1MSPS and an acquisition time of 30 minutes? (about 2Giga samples)

This is the sample code I've been using for short acquisitions (I also transmit samples so that I have sth useful to record). The question is which is the recommended way to extend the acquisition time without loosing samples.

I am using ubuntu 20.04.01

import numpy as np
import adi
import matplotlib.pyplot as plt
import scipy
from import wavfile

sample_rate = 960000 # Hz
center_freq = 915000000 # Hz

acq_secs = 1 # acquisition length in seconds
num_samps = int(acq_secs * sample_rate) # number of samples per call to rx()
rf_bw = 200000 # Hz J

sdr = adi.Pluto("ip:")
sdr.sample_rate = int(sample_rate)

# Config Tx
sdr.tx_rf_bandwidth = int(rf_bw) # filter cutoff, just set it to the same as sample rate
sdr.tx_lo = int(center_freq)
sdr.tx_hardwaregain_chan0 = -50 # Increase to increase tx power, valid range is -90 to 0 dB

# Config Rx
sdr.rx_lo = int(center_freq)
sdr.rx_rf_bandwidth = int(sample_rate)
sdr.rx_buffer_size = num_samps
sdr.gain_control_mode_chan0 = 'manual'
sdr.rx_hardwaregain_chan0 = 0.0 # dB, increase to increase the receive gain, but be careful not to saturate the ADC

# Create transmit waveform (QPSK, 16 samples per symbol)
num_symbols = 1000
x_int = np.random.randint(0, 4, num_symbols) # 0 to 3
x_degrees = x_int*360/4.0 + 45 # 45, 135, 225, 315 degrees
x_radians = x_degrees*np.pi/180.0 # sin() and cos() takes in radians
x_symbols = np.cos(x_radians) + 1j*np.sin(x_radians) # this produces our QPSK complex symbols
samples = np.repeat(x_symbols, 16) # 16 samples per symbol (rectangular pulses)
samples *= 2**14 # The PlutoSDR expects samples to be between -2^14 and +2^14, not -1 and +1 like some SDRs

# Start the transmitter
sdr.tx_cyclic_buffer = True # Enable cyclic buffers
sdr.tx(samples) # start transmitting

# Clear buffer just to be safe
for i in range (0, 10):
raw_data = sdr.rx()

# Receive samples
rx_samples = sdr.rx()

# save IQ rec to disk
wav_samples = np.zeros((num_samps, 2), dtype=np.float32)

wav_samples[...,0] = rx_samples.real
wav_samples[...,1] = rx_samples.imag'out.wav', int(sample_rate), wav_samples)

# Stop transmitting
  • To limit the probability of dropped samples, you want to reduce the number of transfers that occur and increase the elasticity of the buffering. The best way to do that is to make the buffers as large as possible but also have a reasonable number of buffers as well. The default value of 4 buffers should be fine but I would not go smaller. Setting the number of samples large (2^20) would be a good choice as well.

    With respect to the capture script itself, the best possible option would probably to run the script on Pluto itself and writing to a USB stick. Removing the transfer operation back to a PC and any contention issues on the PC itself. However, this would require going to C/C++.
    Now with that said, running python remotely should be fine. However, I would recommend checking for possible overflows (dropped samples) on Pluto itself while running your python code remotely. On the RX side this is done by reading out register 0x80000088 and checking for 3rd bit being set of the cf-ad9361-lpc device.

    To update the script I would do the following for the RX loop:

    time_per_capture_seconds = sdr.rx_buffer_size / sdr.sample_rate
    time_to_capture_over_seconds = 30
    captures = int(time_to_capture_over_seconds / time_per_capture_seconds)

    all_data = np.zeros((sdr.rx_buffer_size, captures), dtype=np.complex64)

    for i in range(captures):
        data = sdr.rx()
        all_data[:, i] = data

    To check for overflows on Pluto you can use this command in a loop:
    iio_reg cf-ad9361-lpc 0x80000088

    Be careful not to max out the ARM processor as that will lead to dropped samples


  • Hi Travis,

    Thank you so much for the detailed answer.

    So far I am triying with the python modification you suggested. I am testing it and everything looks fine for the moment. Still without checking the indicated register for overflow / droopped samples.

    I have another related question. Please let me know if I should open a different thread or post for it:

    In the case above, I understand that the IQ recording process blocks until it is finished. To solve that, I've seen python scripts for other SDR devices which do something as I copy below (RTL SDR case), performing the recording using os commands. My question is, is this also possible with Pluto SDR on linux? Is there any example to trigger taht continuous recording of lets say 30 minutes?

        filename = "{} {}".format(name, str(datetime.utcnow())) 
    os.system("cd .. ; cd recordings ; timeout --preserve-status {} rtl_fm -M fm -s 40k -f {}M -r 32k -g 30 "
    "| sox -v 0.8 -t raw -e signed-integer -c 1 -b 16 -r 32k - \"{}.wav\" &"
    .format(str(timeout), str(freq), filename)) # use rtl_fm to record sdr

    thanks in advance!

  • So the & operator just forks a background process. You could do that when you invoke python itself or leverage something like the screen command.


  • Thanks Travis for the quick reply.

    I will then look to the background process fork you suggest.

    I've also found that iio_readdev would also do the job from the command line. Is there a recommended way or baseline example on how to use iio_readdev for 30min long IQ capture?

    Thanks again