Python UHD API
In Engee Python is supported (for more information, see Working with Python). To manage the SDR USRP RITM in the Python environment, the library is used uhd. The main problem when working with the RITM SDR USRP in Python is delays in simultaneous transmission and reception of the signal. It can be solved by dividing tasks into two independent processes:
-
data transfer;
-
reception, processing and visualization of data (using
matplotlib).
At the beginning of the work, you need to create an SDR management object. In Python, this is done through a class uhd.usrp.MultiUSRP. During initialization, the IP address of the device is specified:
usrp = uhd.usrp.MultiUSRP("addr=192.168.2.100")
Next, you need to adjust the sampling rate, the center frequency of the carrier, and the gain for the receive (RX) and transmit (TX) paths:
usrp.set_tx_rate(SAMP_RATE)
usrp.set_rx_rate(SAMP_RATE)
usrp.set_tx_freq(uhd.types.TuneRequest(CENTER_FREQ))
usrp.set_rx_freq(uhd.types.TuneRequest(CENTER_FREQ))
usrp.set_tx_gain(TX_GAIN)
usrp.set_rx_gain(RX_GAIN)
Streamer objects are created for direct transmission and reception of samples. They specify data formats. This example shows the usage of the format fc32 (complex floating-point numbers) for calculations in Python and sc16 (16-bit integers) for transmission over the network.
st_args = uhd.usrp.StreamArgs("fc32", "sc16")
tx_streamer = usrp.get_tx_stream(st_args)
rx_streamer = usrp.get_rx_stream(st_args)
We generate a test single-tone sinusoidal signal. The library is used for this numpy. We create an array of complex numbers offset relative to the carrier frequency by a given shift.
t = np.arange(BUFFER_SIZE) / actual_rate tx_data = (AMPLITUDE * np.exp(1j * 2 * np.pi * TONE_OFFSET * t)).astype(np.complex64)
To ensure smooth operation SDR without interface delays, the control logic is transferred to a separate process mp.Process. Data exchange with the graphical shell takes place via Shared Memory (shared memory). This allows the GUI process to instantly access the received selections.
existing_shm = shared_memory.SharedMemory(name=shm_name) shared_array = np.ndarray((BUFFER_SIZE,), dtype=np.complex64, buffer=existing_shm.buf)
Two parallel threads are started:
-
Transmitter: Infinitely sends the generated sine wave array to the transmitter path using the function
tx_streamer.send. -
Receiver: Constantly polls the receiver buffer with the command
rx_streamer.recvand writes the received data to the shared memory.
The main process of the application is reading data from shared memory and visualizing it. The fast Fourier transform (FFT) is used to analyze the spectrum using the Blackman window function usage to eliminate spectral leaks.
spec = np.fft.fftshift(np.fft.fft(local_snap * np.blackman(BUFFER_SIZE))) pwr = 20 * np.log10(np.abs(spec) / BUFFER_SIZE + 1e-12)
The result of the work are two graphs: the waveform of the signal and its spectral representation, updated in real time.