USRP in Python — PySDR: A Guide to SDR and DSP ...
USRP in Python — PySDR: A Guide to SDR and DSP ...
In this chapter we learn how to use the UHD Python API to control and receive/transmit signals with a USRP which is a series of SDRs made by Ettus Research (now part of NI). We will discuss transmitting and receiving on the USRP in Python, and dive into USRP-specific topics including stream arguments, subdevices, channels, 10 MHz and PPS synchronization.
If you used the standard from-source install, the following command should benchmark the receive rate of your USRP using the Python API. If using 56e6 caused many dropped samples or overruns, try lowering the number. Dropped samples arent necessarily going to ruin anything, but its a good way to test the inefficiencies that might come with using a VM or older computer, for example. If using a B 2X0, a fairly modern computer with a USB 3.0 port running properly should manage to do 56 MHz without dropped samples, especially with num_recv_frames set so high.
For more help see Ettus official Building and Installing UHD from source page. Note that there are also methods of installing the drivers that dont require building from source.
For USB type USRPs youll need to install VM guest additions. Within the VM go to Devices > Insert Guest Additions CD > hit run when a box pops up. Follow the instructions. Restart the VM, then attempt to forward the USRP to the VM, assuming it shows up in the list under Devices > USB. The shared clipboard can be enabled through Devices > Shared Clipboard > Bidirectional.
Under system > processor > choose at least 3 CPUs. If you have an actual video card then in display > video memory > choose something much higher.
Start the VM. It will ask you for installation media. Choose the Ubuntu 22 desktop .iso file. Choose install Ubuntu, use default options, and a pop up will warn you about the changes you are about to make. Hit continue. Choose name/password and then wait for the VM to finish initializing. After finishing the VM will restart, but you should power off the VM after the restart.
Create the virtual hard disk, choose VDI, and dynamically allocate size. 15 GB should be enough. If you want to be really safe you can use more.
While the Python code provided in this textbook should work under Windows, Mac, and Linux, we will only be providing driver/API install instructions specific to Ubuntu 22 (although the instructions below should work on most Debian-based distributions). We will start by creating an Ubuntu 22 VirtualBox VM; feel free to skip the VM portion if you already have your OS ready to go. Alternatively, if youre on Windows 11, Windows Subsystem for Linux (WSL) using Ubuntu 22 tends to run fairly well and supports graphics out-of-the-box.
Receiving samples off a USRP is extremely easy using the built-in convenience function recv_num_samps(), below is Python code that tunes the USRP to 100 MHz, using a sample rate of 1 MHz, and grabs 10,000 samples off the USRP, 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
(
samples
[
0
:
10
])
The [0] is telling the USRP to use its first input port, and only receive one channel worth of samples (for a B210 to receive on two channels at once, for example, you could use [0, 1]).
Heres a tip if you are trying to receive at a high rate but are getting overflows (Os are showing up in your console). Instead of usrp = uhd.usrp.MultiUSRP()
, use:
usrp
=
uhd
.
usrp
.
MultiUSRP
(
"num_recv_frames="
)
which makes the receive buffer much larger (the default value is 32), helping to reduce overflows. The actual size of the buffer in bytes depends on the USRP and type of connection, but simply setting num_recv_frames
to a value much higher than 32 tends to help.
For more serious applications I recommend not using the convenience function recv_num_samps(), because it hides some of the interesting behavior going on under the hood, and there is some set up that happens each call that we might only want to do once at the beginning, e.g., if we want to receive samples indefinitely. The following code has the same functionality as recv_num_samps(), in fact its almost exactly what gets called when you use the convenience function, but now we have the option to modify the behavior:
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
)
# Set up the 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
,
),
Suggested reading:
Why is LFP32140 Lithium iron phosphate battery Better?
Everything that you need to know about #12: Woven Filter ...
How to Select An Induction Heating System for Your Project?If you are looking for more details, kindly visit Highmesh.
dtype
=
np
.
complex64
)
# Start Stream
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
)
(
len
(
samples
))
(
samples
[
0
:
10
])
With num_samps set to 10,000 and the recv_buffer set to , the for loop will run 10 times, i.e., there will be 10 calls to streamer.recv. Note that we hard-coded recv_buffer to but you can find the maximum allowed value using streamer.get_max_num_samps()
, which is often around -something. Also note that recv_buffer must be 2d because the same API is used when receiving multiple channels at once, but in our case we just received one channel, so recv_buffer[0] gave us the 1D array of samples that we wanted. You dont need to understand too much about how the stream starts/stops for now, but know that there are other options besides continuous mode, such as receiving a specific number of samples and having the stream stop automatically. Although we dont process metadata in this example code, it contains any errors that occur, among other things, which you can check by looking at metadata.error_code at each iteration of the loop, if desired (errors tend to also show up in the console itself, as a result of UHD, so dont feel like you have to check for them within your Python code).
Receive Gain¶
The following list shows the gain range of the different USRPs, they all go from 0 dB to the number specified below. Note that this is not dBm, its essentially dBm combined with some unknown offset because these are not calibrated devices.
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
You can also use the command uhd_usrp_probe
in a terminal and in the RX Frontend section it will mention the gain range.
When specifying the gain, you can use the normal set_rx_gain() function which takes in the gain value in dB, but you can also use set_normalized_rx_gain() which takes in a value from 0 to 1 and automatically converts it to the range of the USRP youre using. This is convenient when making an app that supports different models of USRP. The downside of using normalized gain is that you no longer have your units in dB, so if you want to increase your gain by 10 dB, for example, you now have to calculate the amount.
Automatic Gain Control¶
Some USRPs, including the B200 and E310 series, support automatic gain control (AGC) which will automatically adjust the receive gain in response to the received signal level, in an attempt to best fill the ADCs bits. AGC can be turned on using:
usrp
.
set_rx_agc
(
True
,
0
)
# 0 for channel 0, i.e. the first channel of the USRP
If you have a USRP that does not implement an AGC, an exception will be thrown when running the line above. With AGC on, setting the gain wont do anything.
Stream Arguments¶
In the full example above youll see the line st_args = uhd.usrp.StreamArgs("fc32", "sc16")
. The first argument is the CPU data format, which is the data type of the samples once they are on your host computer. UHD supports the following CPU data types when using the Python API:
Stream Arg
Numpy Data Type
Description
fc64
np.complex128
Complex-valued double-precision data
fc32
np.complex64
Complex-valued single-precision data
You might see other options in documentation for the UHD C++ API, but these were never implemented within the Python API, at least at the time of this writing.
The second argument is the over-the-wire data format, i.e. the data type as the samples are sent over USB/Ethernet/SFP to the host. For the Python API, the options are: sc16, sc12, and sc8, with the 12 bit option only supported by certain USRPs. This choice is important because the connection between the USRP and host computer is often the bottleneck, so by switching from 16 bits to 8 bits you might achieve a higher rate. Also remember that many USRPs have ADCs limited to 12 or 14 bits, using sc16 doesnt mean the ADC is 16 bits.
For the channel portion of the st_args
, see the Subdevice and Channels subsection below.
USRP Source
The USRP Source Block is used to stream samples from a USRP device (i.e. act as the receiver).
There is no need to use a Throttle block when a hardware source like a USRP Source is used, because the USRP acts as the throttle.
There are two methods of setting parameters and adjusting them while running.
- Variables and GUI Widgets
- Parameters may be set by a Variable block, a GUI Widget such as QT GUI Range, or directly in the block Properties.
- Messages
- The input message port called "command" can be used to change frequency, gain, and other parameters via Message Passing. A complete list of message commands can be found in Common command keys.
- A common arrangement is to use the QT GUI Sink's output message port to connect to the USRP Source's input port, so that when a user double-clicks within the GUI to change frequency, the change gets propagated to the USRP device. The Example Flowgraph section shows this and other message-based methods.
Parameters
- Output Type
- This parameter controls the data type of the output stream in GNU Radio.
- Wire Format
- This parameter controls the form of the data over the bus/network. Complex bytes may be used to trade off precision for bandwidth. Not all formats are supported on all devices.
- Stream Args
- Optional arguments to be passed in the UHD streamer object. Streamer args is a list of key/value pairs; usage is determined by the implementation. Docs for stream args
- Stream Channels
- Optionally used to specify which channels are used, e.g. [0,1]
- Devices Address
- The device address is a delimited string used to locate UHD devices on your system. If left blank, the first UHD device found will be used. Use the device address to specify a specific device or list of devices.
Examples: serial= addr=192.168.10.2 addr0=192.168.10.2, addr1=192.168.10.3
- Device Arguments
- Other various arguments that can be passed to the USRP Source, see USRP Device Configuration
- Sync
- Can be used to get USRP to attempt to sync to either PC's clock, or PPS signal if it exists.
- Clock Rate [Hz]
- The clock rate shouldn't be confused with sample rate, but they are related. The B2X0 and E31X USRPs use a flexible clock rate that will either be equal to the requested sample rate, or a multiple of it. Best left to default unless specific behavior is needed.
- Num Mboards
- Selects the number of USRP motherboards (i.e. physical USRP devices) in this device configuration.
- Mbx Clock Source
- Where the motherboard should sync its clock references. External refers to the 10 MHz input on the USRP. O/B GPSDO is the optional onboard GPSDO module, which provides its own 10 MHz (and PPS) signals.
- Mbx Time Source
- Where the motherboard should sync its time references. External refers to the PPS input on the USRP. O/B GPSDO is the optional onboard GPSDO module, which provides its own PPS (and 10 MHz) signals.
- Mbx Subdev Spec
- Each motherboard should have its own subdevice specification and all subdevice specifications should be the same length. Select the subdevice or subdevices for each channel using a markup string. The markup string consists of a list of dboard_slot:subdev_name pairs (one pair per channel). If left blank, the UHD will try to select the first subdevice on your system. See the application notes for further details.Single channel example: ":AB"
- Num Channels
- Selects the total number of channels in this multi-USRP configuration. Ex: 4 motherboards with 2 channels per board = 8 channels total.
- Sample Rate
- The number of samples per second, which is equal to the bandwidth in Hz we wish to observe. The UHD device driver will try its best to match the requested sample rate. If the requested rate is not possible, the UHD block will print an error at runtime.
- Chx Center Frequency
- The center frequency is the overall frequency of the RF chain. The base option is to input an int or float value in Hz. But it is also possible to pass a tune_request object for greater control on how the driver tunes the elements in the RF chain.
- Examples:
- Tuning with an LO offset example:
uhd.tune_request(f_target, f_offset)
- Tuning without DSP:
uhd.tune_request(target_freq, dsp_freq=0, dsp_freq_policy=uhd.tune_request.POLICY_MANUAL)
- Chx Gain Value
- Value used for gain, which is either between 0 and the max gain for the USRP (usually around 70 to 90), when using the default "Absolute" Gain Type. When using "Normalized" Gain Type, it will always be 0.0 to 1.0, where 1.0 will be mapped to the max gain of the USRP being used.
- Chx Gain Type
- Absolute (in dB) or Normalized (0 to 1)
- Chx Antenna
- For subdevices with only one antenna, this may be left blank. Otherwise, the user should specify one of the possible antenna choices. See the daughter-board application notes for the possible antenna choices.
- Chx Bandwidth
- The bandwidth used by the USRP's anti-aliasing filter. To use the default bandwidth filter setting, this should be zero. Only certain subdevices have configurable bandwidth filters. See the daughterboard application notes for possible configurations.
- Chx Enable DC Offset Correction
- Attempts to remove the DC offset, i.e. the average value of a signal, which is something that will be very visible in the Frequency domain.
- Chx Enable IQ Imbalance Correction
- Attempts to correct any IQ imbalance, which is when there is a mismatch between the I and Q signal paths, typically causing a stretching effect to a constellation.
Example Flowgraph
The example flowgraph below is taken from Guided_Tutorial_Hardware_Considerations. It shows how the message ports can be connected so that changing the frequency from within the QT GUI Sink (by double clicking on the X-axis) is propagated to the USRP. Even though it is hidden by the GUI Sink block, there is a connection from the output message port (freq) on the left to the input message port (freq) on the right. Variables are used to set the sample rate, RF gain, and initial tuning.
A significant drawback to the method above is that once a frequency is selected by double clicking, the frequency displayed in the GUI Range is no longer correct. To remedy this situation, messages can be used to change all of the run-time functions. Two approaches are presented here. The first is to use a QT GUI Message Edit Box. Not only can it generate commands, but it can display commands from other sources such as a QT GUI Sink. That way, the parameter (such as frequency) is always displayed correctly. The following flowgraph using this method not only sets the frequency, but also the RF gain.
A second message-based method can be found in UHD Message Tuner. Each of the parameters is set using a QT GUI Entry block. Every two seconds, the Message Strobe block sends all of the parameters to the USRP. Note that the initial frequency and gain values must be set using Variable blocks.
Source Files
The company is the world’s best USRP B Products supplier. We are your one-stop shop for all needs. Our staff are highly-specialized and will help you find the product you need.
- Header files
- usrp_source_impl.h
- Public header files
- usrp_source.h