Sign in
Explore Diverse Guest Blogging Opportunities on Our Online Diary Platform
Explore Diverse Guest Blogging Opportunities on Our Online Diary Platform
Your Position: Home - Agriculture - USRP in Python — PySDR: A Guide to SDR and DSP ...
Guest Posts

USRP in Python — PySDR: A Guide to SDR and DSP ...

In this section, we explore how to effectively utilize the UHD Python API for controlling and transmitting or receiving signals with the USRP, a line of software-defined radios (SDRs) designed by Ettus Research, which is now part of NI. We will explain the process of sending and receiving data using the USRP within a Python environment and delve into intricacies such as stream parameters, subdevice configurations, channel settings, and methods for 10 MHz and PPS synchronization.

If you installed the drivers using the standard from-source method, the following command can benchmark the reception rate of your USRP via the Python API. Should you experience dropped samples or overruns with a setting of 56e6, consider reducing that figure. While missing samples won't necessarily disrupt your operations, it serves as a diagnostic tool for identifying performance issues that could arise from using a VM or older hardware, for instance. For basic models like the B210, a reasonably modern computer equipped with a functioning USB 3.0 port should handle a rate of 56 MHz without any sample loss, especially if the num_recv_frames parameter is set to a sufficiently high value.

For troubleshooting assistance, refer to Ettus' official guide on Building and Installing UHD from source, as there are alternative installation methods that do not require building from source code.

For USB-connected USRPs, you will need to install Virtual Machine guest additions. Within the virtual environment, navigate to Devices > Insert Guest Additions CD and execute it upon prompt. Complete the setup, restart your VM, and attempt to connect the USRP to the VM, checking if it appears under Devices > USB. You may also enable a shared clipboard via Devices > Shared Clipboard > Bidirectional.

To enhance your system configuration, go to System > Processor and select at least three CPUs. If your setup includes a dedicated graphics card, increase the video memory under Display > Video Memory accordingly.

Initiate the VM setup. When prompted for installation media, select the Ubuntu 22 desktop .iso file and proceed with the "Install Ubuntu" option, following the default installation steps. A confirmation dialog will appear regarding the changes to be applied; click continue. After setting a username and password, allow the VM to finish initializing, and power it off after the restart.

Next, create a virtual hard disk, select VDI, and dynamically allocate the size; 15 GB is sufficient for most scenarios, but more space can be assigned for additional safety.

The Python examples in this guide are compatible with Windows, Mac, and Linux environments; however, installation instructions provided here are tailored specifically for Ubuntu 22, although they can generally be adapted to most Debian-based distributions. This guide will illustrate the setup of an Ubuntu 22 VirtualBox VM, but feel free to skip this section if your operating system is already prepared. Alternatively, if using Windows 11, the Windows Subsystem for Linux (WSL) with Ubuntu 22 tends to function efficiently and supports graphics by default.

Extracting samples from a USRP is quite straightforward with the convenient function recv_num_samps(). Below is a Python code snippet that configures the USRP to a 100 MHz frequency with a 1 MHz sample rate and collects 10,000 samples using a receive gain of 50 dB:

import

uhd

usrp

=

uhd

.

usrp

.

MultiUSRP

()

samples

=

usrp

.

recv_num_samps

(

,

100e6

,

1e6

,

[

0

],

50

)

# units: N, Hz, Hz, list of channel IDs, dB

print

(

samples

[

0

:

10

])

The input channel index [0] specifies that the USRP should utilize its first input port. Receiving on multiple channels simultaneously, such as on a B210, can be achieved by using the indices [0, 1].

Should you attempt to receive data at a high rate and encounter overflows (indicated by O's appearing in your console), consider replacing usrp = uhd.usrp.MultiUSRP() with:

usrp

=

uhd

.

usrp

.

MultiUSRP

(

"num_recv_frames="

)

This adjustment increases the size of the receive buffer (from the default value of 32), which helps to alleviate overflow issues. The buffer's actual size in bytes is dependent on the model of the USRP and the connection type, but setting num_recv_frames to a higher value than 32 is often beneficial.

For more demanding applications, it is advisable to avoid using the convenience function recv_num_samps(), as it abstracts some of the intriguing processes occurring in the background. Additionally, some setup occurs for each call, which may be redundant if we intend to gather samples continuously. The code below performs similarly to recv_num_samps(), but it allows greater customization:

import

uhd

import

numpy

as

np

usrp

=

uhd

.

usrp

.

MultiUSRP

()

num_samps

=

# number of samples received

center_freq

=

100e6

# Hz

sample_rate

=

1e6

# Hz

gain

=

50

# dB

usrp

.

set_rx_rate

(

sample_rate

,

0

)

usrp

.

set_rx_freq

(

uhd

.

libpyuhd

.

types

.

tune_request

(

center_freq

),

0

)

usrp

.

set_rx_gain

(

gain

,

0

)

# Prepare stream and receive buffer

st_args

=

uhd

.

usrp

.

StreamArgs

(

"fc32"

,

"sc16"

)

st_args

.

channels

=

[

0

]

metadata

=

uhd

.

types

.

RXMetadata

()

streamer

=

usrp

.

get_rx_stream

(

st_args

)

recv_buffer

=

np

.

zeros

((

1

,

),

dtype

If you desire to discover more, feel free to visit our website Highmesh.

Additional reading:
Sodium Ion Battery vs. Lithium Ion Battery
What is FSK insulation facing?
How to Choose What Is The Process of Lost Foam or Expandable Pattern Casting?

=

np

.

complex64

)

# Start Streaming

stream_cmd

=

uhd

.

types

.

StreamCMD

(

uhd

.

types

.

StreamMode

.

start_cont

)

stream_cmd

.

stream_now

=

True

streamer

.

issue_stream_cmd

(

stream_cmd

)

# Receive Samples

samples

=

np

.

zeros

(

num_samps

,

dtype

=

np

.

complex64

)

for

i

in

range

(

num_samps

//

):

streamer

.

recv

(

recv_buffer

,

metadata

)

samples

[

i

*

:(

i

+

1

)

*

]

=

recv_buffer

[

0

]

# Stop Stream

stream_cmd

=

uhd

.

types

.

StreamCMD

(

uhd

.

types

.

StreamMode

.

stop_cont

)

streamer

.

issue_stream_cmd

(

stream_cmd

)

print

(

len

(

samples

))

print

(

samples

[

0

:

10

])

With num_samps set to 10,000 and the recv_buffer dimensioned accordingly, the for loop will execute 10 iterations, resulting in 10 separate calls to streamer.recv. Note that we've initialized the recv_buffer to an arbitrary dimension, but it's advisable to verify the maximum permissible size using streamer.get_max_num_samps(), often found to be around an elusive high number. Furthermore, the recv_buffer must have two dimensions since the same API is utilized for receiving on multiple channels; however, in our example, we only processed a single channel, allowing us to extract a 1D array of samples from recv_buffer[0]. While understanding the startup and shutdown processes of streams isn't critical at this stage, it is notable that other modes exist beyond "continuous," including receiving a specific sample count that triggers automatic stream termination. Although we don’t address metadata in this instance, it does capture error codes and other vital information during data reception, which can be inspected through metadata.error_code in every loop iteration, if needed, as typical error notifications surface in the console output due to UHD.

When setting the receive gain, different USRP models have specific gain ranges stretching from 0 dB up to various maximums. It's important to recognize that these measurements don't reflect dBm; it's essentially dBm combined with some unknown offset due to the devices lacking formal calibration. Below, find gain ranges for various USRP models:

  • B200/B210/B200-mini: 76 dB

  • X310/N210 with WBX/SBX/UBX: 31.5 dB

  • X310 with TwinRX: 93 dB

  • E310/E312: 76 dB

  • N320/N321: 60 dB

Additionally, by employing the command uhd_usrp_probe in a terminal, the RX Frontend section will provide insights into the gain range available for your device. To set the gain, you can use the standard set_rx_gain() function, which requires a gain value in dB. Alternatively, the set_normalized_rx_gain() function allows entry from 0 to 1, translating to the specific gain range of your USRP, which is convenient when developing applications that support various models. However, employing normalized gain may lead to a lack of clarity in unit representation, necessitating calculations to determine the precise dB increment.

Certain USRP models, including the B200 and E310 families, are equipped with Automatic Gain Control (AGC), which adjusts receive gain based on the incoming signal's amplitude to effectively maximize the ADC's dynamic range. You can activate AGC with:

usrp

.

set_rx_agc

(

True

,

0

)

# 0 for channel 0, the USRP's first channel

If your USRP model lacks AGC functionality, executing the command will yield an exception. When AGC is enabled, adjusting the gain has no effect.

In the example showcased earlier, you will notice the line st_args = uhd.usrp.StreamArgs("fc32", "sc16"). Here, the first argument denotes the CPU data format—the data type of the samples once transferred to your host system. The UHD API supports different CPU data types when utilizing Python API:

Stream Arg

Numpy Data Type

Description

fc64

np.complex128

Complex-valued double-precision data

fc32

np.complex64

Complex-valued single-precision data

It’s worth noting that while other options exist within the UHD C++ API documentation, they may not have been integrated within the Python API at the time of this writing.

The second argument signifies the "over-the-wire" data format—the sample type transmitted over the connection (USB/Ethernet/SFP) to the host. For Python API, the available options include "sc16," "sc12," and "sc8," with the 12-bit format supported only by selective USRPs. Choosing the appropriate format is crucial because it can often influence the transfer rate between the USRP and your host computer, thus higher throughput may be achieved by switching from 16 bits to 8 bits. Additionally, be aware that many USRPs utilize ADCs limited to 12 or 14 bits; hence the use of "sc16" does not necessarily indicate a 16-bit ADC.

For details on the channels included in st_args, consult the subsection addressing Subdevices and Channels below.

Comments

0 of 2000 characters used

All Comments (0)
Get in Touch

  |   Transportation   |   Toys & Hobbies   |   Tools   |   Timepieces, Jewelry, Eyewear   |   Textiles & Leather Products   |   Telecommunications   |   Sports & Entertainment   |   Shoes & Accessories   |   Service Equipment