Technical Documentation

Developing A Usrp B210 Rf Interference Analysis Application On Windows

Technical guide covering **developing a usrp b210 rf interference analysis application on windows**

👤
Author
Cosmic Lounge AI Team
📅
Updated
6/1/2025
⏱️
Read Time
11 min
Topics
#ai #api #server #setup #installation #configuration #development #code #introduction

📖 Reading Mode

📖 Table of Contents

🌌 Developing a USRP B210 RF Interference Analysis Application on Windows



🌟 1. Introduction

🚀 Welcome to this comprehensive guide! This section will give you the foundational knowledge you need. This guide provides comprehensive, step-by-step instructions for developing a basic radio frequency (RF) interference analysis application for the Windows platform using an Ettus Research USRP B210 Software-Defined Radio (SDR).

The primary objective is to equip developers with the knowledge to set up the necessary environment, interface with the USRP hardware, acquire I/Q data, perform fundamental spectrum analysis, and interpret the results to identify potential interference sources and characterize the baseline RF environment. The focus will be on scanning the 4.2 GHz frequency range, relevant for certain drone operations, while also presenting techniques applicable to common drone bands like 900 MHz, 2.4 GHz, and 5.8 GHz.



🌟 2. Windows Development Environment Setup

A correctly configured development environment is paramount for successful SDR application development on Windows. This involves installing the hardware drivers for the USRP B210 and setting up the chosen programming language and associated libraries.

2.1. Installing Ettus UHD Drivers for USRP B210

The USRP Hardware Driver (UHD) is the core software component that enables communication between the host computer and the USRP hardware. Proper installation and configuration are essential. The recommended approach for Windows is using the pre-compiled binary installer provided by Ettus Research (now part of National Instruments).

⚡ Steps for Binary Installation and Driver Setup:

1. Download UHD Installer: Obtain the latest stable UHD installer for Windows (64-bit recommended) from the official Ettus Research file server.1 Navigate to http://files.ettus.com/binaries/uhd/latest\_release/. Select an installer matching your system architecture (e.g., Win64) and preferred Visual Studio runtime version (e.g., VS2019, VS2022). The filename typically encodes this information (e.g., uhd_4.8.0.0-release_Win64_VS2019.exe).1 2. Install UHD: Run the downloaded executable installer. Follow the on-screen prompts. The default installation path is typically C:\Program Files\UHD.1 3. Install MSVC Redistributable: Download and install the Microsoft Visual C++ Redistributable package that corresponds to the Visual Studio version indicated in the UHD installer filename (e.g., VS2019).1 This ensures the necessary runtime libraries are available. Links can usually be found on the Microsoft website.1 4. Install USB Driver: The USRP B210 connects via USB 3.0. A specific WinUSB-based driver is required for UHD to recognize the device.

  • Starting with UHD 4.8, the necessary driver files are typically included within the UHD installation directory, usually under share\uhd\usbdriver (e.g., C:\Program Files\UHD\share\uhd\usbdriver).1 Alternatively, the driver package (erllc_uhd_winusb_driver.zip) can be downloaded separately.1

  • Connect the USRP B210 to a USB 3.0 port on your computer.4

  • Open the Windows Device Manager. Locate the USRP B210, which will likely appear as an unrecognized USB device.1

  • Right-click the unrecognized device and select “Update driver”.1

  • Choose “Browse my computer for driver software”.1

  • Navigate to the share\uhd\usbdriver folder within your UHD installation directory.1

  • Select the appropriate .inf file for the B210 (e.g., ettus_b210.inf or similar) and proceed with the installation.1

  • If the device is still not recognized after driver installation, try restarting the computer.5

⚡ Verification:

Once UHD and the USB driver are installed, verify that the system can detect the USRP B210.

1. Open Command Prompt: Launch a Windows Command Prompt (cmd.exe). 2. Navigate to UHD Bin Directory: Change directory to the bin folder within your UHD installation path (e.g., cd “C:\Program Files\UHD\bin”).1 3. Run uhd_find_devices: Execute the command uhd_find_devices.exe.1 A successful detection will list your USRP B210, including its type and serial number.1 If it reports “No UHD Devices Found”, double-check the USB connection, driver installation (Device Manager status), and potentially try a different USB port or cable.8 Ensure firewalls are not blocking discovery if using networked devices, though less relevant for USB B210.3 Ensure the UHD version supports the B210 (requires >= 3.6.0, latest stable recommended).4 4. Run uhd_usrp_probe: Execute the command uhd_usrp_probe.exe.1 This utility provides more detailed information about the device, including firmware/FPGA versions, available sensors, and front-end characteristics.3 Successful output confirms communication and readiness. Correctly installing and verifying the UHD drivers ensures the foundational layer for software interaction with the USRP B210 is operational.

2.2. Choosing and Configuring the Programming Environment

Several programming languages and libraries can be used for SDR development on Windows. Key contenders for this project are Python with UHD API bindings or SoapySDR, and C++ using the native UHD API.

⚡ Evaluation: Python vs. C++

  • Python (with UHD API or SoapySDR):
  • Ease of Setup: Generally easier. Installing Python and required packages (NumPy, SciPy, Matplotlib, UHD/SoapySDR bindings) via pip or package managers like Conda is straightforward.2 UHD provides official Python wheels via PyPI starting from version 4.8.1
  • Development Speed: Significantly faster due to Python’s simpler syntax, dynamic typing, and extensive libraries for tasks like plotting and data analysis.17 Ideal for rapid prototyping and experimentation.18
  • Performance: Interpreted nature makes raw Python code slower than compiled C++ for CPU-intensive tasks.17 However, critical SDR processing often relies on underlying C/C++ libraries (like UHD itself or NumPy/SciPy FFT implementations), mitigating performance bottlenecks.18 The UHD Python API releases the Global Interpreter Lock (GIL) during performance-critical calls like recv() to improve throughput.15 Performance is generally sufficient for many SDR tasks, including real-time streaming at moderate sample rates.19 Potential issues with dropped packets can occur at high rates if Python processing can’t keep up.23
  • Library Support: Excellent ecosystem. NumPy/SciPy provide powerful numerical and signal processing capabilities.18 Matplotlib is standard for plotting.27 Direct UHD Python bindings are officially supported.2 SoapySDR provides a hardware abstraction layer with Python bindings, supporting various SDRs including USRPs via SoapyUHD.14
  • Examples: Numerous examples are available for Python SDR, including within the UHD repository (host/examples/python) 19 and online resources like PySDR.12
  • C++ (with native UHD API):
  • Ease of Setup: More complex. Requires setting up a C++ compiler (MSVC via Visual Studio recommended and officially supported by UHD 37), CMake build system, and managing dependencies like Boost and LibUSB, either by building UHD from source or carefully linking against pre-built UHD libraries/headers.37 Visual Studio setup involves installing the “Desktop development with C++” workload.38
  • Development Speed: Slower due to compilation cycles, stricter syntax, and manual memory management (though modern C++ mitigates some of this).17
  • Performance: Generally offers the highest potential performance due to direct compilation to machine code and fine-grained control over resources.17 Preferred for computationally demanding real-time applications or resource-constrained environments.18
  • Library Support: Strong support via UHD native API. Boost libraries are often required.37 Integration with numerical/plotting libraries might require more effort than in Python.
  • Examples: UHD includes C++ examples (host/examples).3 Many core GNU Radio blocks are written in C++.43

⚡ Recommendation:

For this project’s goal – developing a simple RF analysis application with a focus on clear, actionable guidance and practical examples – Python with the native UHD API bindings is the recommended approach. Its significantly easier setup, faster development cycle, and excellent library support for analysis and visualization outweigh the potential raw performance benefits of C++ for this specific task.

⚡ Python Environment Setup Steps:

1. Install Python: Download and install a recent version of Python (3.7 or later is required by current UHD versions 38) for Windows from python.org. Choose the 64-bit installer if you installed the 64-bit UHD binary. During installation, ensure you check the option to “Add Python to PATH”.38 2. Verify Python and Pip: Open a new Command Prompt and verify the installation: Bash python —version pip —version pip is Python’s package installer and should be included with recent Python versions. 3. Install Essential Libraries: Install NumPy, SciPy, and Matplotlib using pip: Bash pip install numpy scipy matplotlib These libraries are fundamental for numerical operations, signal processing (FFT/PSD), and plotting.18 4. Install UHD Python API Package:

  • First, determine the exact version of the installed UHD binaries. Run the uhd_config_info.exe utility found in the UHD bin directory: Bash “C:\Program Files\UHD\bin\uhd_config_info.exe” —version Note the version number (e.g., 4.8.0.0).

  • Install the matching UHD Python package (wheel) from PyPI using pip. Replace <version_from_config_info> with the version number obtained above 1: Bash pip install uhd==<version_from_config_info> For example: pip install uhd==4.8.0.0

  • Ensure the Python version and architecture (32/64-bit) match the installed UHD package and the wheel you are installing.1 5. Verify UHD Python API Installation: Open a Python interpreter (type python in the Command Prompt) and try importing the uhd module 1: Python

import uhd print(uhd.__version__) # Optional: Check version matches

exit() If the import succeeds without errors, the Python environment is ready. If you encounter DLL loading errors (especially on Python 3.8+), ensure the UHD bin directory (containing uhd.dll and potentially libusb-1.0.dll) is in the system’s PATH environment variable, or use os.add_dll_directory() in your script before importing uhd.6 Copying libusb-1.0.dll to the UHD bin directory might also be necessary if not already present.2

This setup provides a robust Python environment capable of interfacing with the USRP B210 via the UHD API and performing the required signal processing and analysis tasks.



🌟 3. Python Application Development for RF Analysis

This section details the development of the Python application using the configured environment and the UHD API.

3.1. Core SDR Processing Concepts

The application will implement a basic SDR receiver chain. The fundamental steps involved are:

1. Hardware Configuration: Setting parameters on the USRP B210 (center frequency, sample rate, gain, antenna) to define the portion of the RF spectrum to capture and how it’s digitized. 2. I/Q Data Acquisition: Streaming complex baseband (I/Q) samples from the USRP to the host computer over USB. These samples represent the amplitude and phase of the signal relative to the center frequency. 3. Digital Signal Processing (DSP): Applying mathematical operations to the I/Q samples. The core operation for spectrum analysis is the Fast Fourier Transform (FFT). 4. Spectrum Calculation (PSD): Converting the FFT output into a Power Spectral Density (PSD), which shows the distribution of signal power across different frequencies within the captured bandwidth.24 5. Analysis & Interpretation: Extracting information from the PSD, such as noise floor level, signal peaks (strength and frequency), and identifying patterns indicative of interference.46 6. Output/Visualization: Displaying or logging the results (e.g., PSD plot, detected signals, noise level).

3.2. Interfacing with the USRP B210 using Python UHD API

The first step in the application code is to establish communication with the USRP device and configure its RF front-end parameters.

⚡ Initializing the USRP:

The uhd.usrp. MultiUSRP class is the primary interface in the Python API for interacting with USRP devices.15 Instantiating it without arguments typically finds the first available USRP. For systems with multiple USRPs, device arguments (like the serial number) can be provided to select a specific device.49

Python

import uhd import numpy as np

🌌 --- Initialization ---

try:

🌌 Find and initialize the first available USRP device

🌌 Specify args=“serial=YOUR_SERIAL” if multiple USRPs are connected

usrp = uhd.usrp. MultiUSRP() print(f”Using Device: {usrp.get_pp_string()}”) # Verify device connection [51]

🌌 Optional: Check number of RX channels available

num_rx_channels = usrp.get_rx_num_channels() print(f”Number of RX channels: {num_rx_channels}”) # B210 should report 2 [50]

except RuntimeError as e: print(f”Error initializing USRP: {e}”) print(“Ensure UHD drivers are installed and the device is connected.”) exit()

⚡ Setting Key Parameters:

Before acquiring data, the RF parameters must be configured for the desired channel. On the B210, channel 0 typically corresponds to the RF A side, and channel 1 to the RF B side.52

  • Center Frequency: The target frequency around which the SDR will capture bandwidth. Use usrp.set_rx_freq() along with a uhd.types. TuneRequest object.12 Python target_freq = 4.2e9 # 4.2 GHz (User query target)

channel = 0 # Use RF A port (adjust if using RF B)

tune_request = uhd.types. TuneRequest(target_freq) usrp.set_rx_freq(tune_request, channel)

🌌 Verify the actual tuned frequency

actual_freq = usrp.get_rx_freq(channel) print(f”Set RX Freq: {target_freq/1e6:.3f} MHz | Actual RX Freq: {actual_freq/1e6:.3f} MHz”)

  • Sample Rate: Determines the instantaneous bandwidth captured (BW=Sample Rate). Use usrp.set_rx_rate().12 UHD may adjust the requested rate to one achievable with the current master clock rate.23 The B210 supports up to ~61.44 Msps, but practical limits due to USB 3.0 bandwidth are often lower (e.g., 20-30 Msps might be more stable for continuous streaming).50 Python sample_rate = 10e6 # 10 Msps (adjust as needed)

usrp.set_rx_rate(sample_rate, channel)

🌌 Verify the actual sample rate

actual_rate = usrp.get_rx_rate(channel) print(f”Set RX Rate: {sample_rate/1e6:.3f} Msps | Actual RX Rate: {actual_rate/1e6:.3f} Msps”)

  • RF Gain: Amplifies the received signal before digitization. Use usrp.set_rx_gain().12 The B210 typically has a gain range of 0 to 76 dB.12 A mid-range value (e.g., 30-50 dB) is a reasonable starting point. Too low gain results in weak signals dominated by noise; too high gain can cause clipping/saturation of strong signals or the SDR’s internal amplifiers. Python gain = 40 # dB (adjust based on signal environment)

usrp.set_rx_gain(gain, channel)

🌌 Verify the actual gain setting

actual_gain = usrp.get_rx_gain(channel) print(f”Set RX Gain: {gain} dB | Actual RX Gain: {actual_gain} dB”)

  • Antenna Selection: Selects the physical RF port to use. Use usrp.set_rx_antenna().52 For the B210, each channel (A/B) has a primary TX/RX port and a dedicated RX2 port.52 Common names are “TX/RX” and “RX2”.52 Ensure this matches the physical connection. Python antenna = “RX2” # Or “TX/RX”, depending on connection to RF A or RF B

usrp.set_rx_antenna(antenna, channel)

🌌 Verify the selected antenna

selected_antenna = usrp.get_rx_antenna(channel) print(f”Set RX Antenna: {antenna} | Selected RX Antenna: {selected_antenna}”)

It is good practice to query the actual settings (get_rx_freq, get_rx_rate, etc.) after setting them, as the driver or hardware might make adjustments.23

⚡ Table 2: Key USRP B210 Parameters and UHD Python API Calls

ParameterPython UHD API CallB210 Notes / Example ValueReferences
Center Frequencyusrp.set_rx_freq(tune_request, chan)70 MHz - 6 GHz.52 tune_request = uhd.types. TuneRequest(4.2e9)12
Sample Rateusrp.set_rx_rate(rate, chan)Up to ~61 Msps 50, often USB limited. rate = 10e6. May be coerced.2312
RF Gainusrp.set_rx_gain(gain, chan)0 - 76 dB.12 gain = 40.12
Antenna Selectionusrp.set_rx_antenna(antenna_name, chan)‘TX/RX’, ‘RX2’ for each channel (A/B).52 antenna = “RX2”.48
Master Clock Rateusrp.set_master_clock_rate(rate)Default usually sufficient. rate = 20e6 (Example). 5252

Properly initializing the USRP involves setting these parameters sequentially for the desired channel. Verification confirms the hardware state before proceeding to data acquisition.

3.3. Streaming I/Q Data Acquisition

Once the USRP is configured, the next step is to continuously receive (stream) I/Q samples.

⚡ Configure the Streamer:

A streamer object handles the data flow between the USRP and the host application. It requires configuration regarding data formats and target channels. Python

🌌 --- Streamer Configuration ---

🌌 Specify the format of samples on the host PC (CPU format)

🌌 ‘fc32’ means complex (numpy.complex64)

🌌 Specify the format over the wire (USB)

🌌 ‘sc16’ means complex<int16_t> - common for B210 efficiency

stream_args = uhd.usrp. StreamArgs(“fc32”, “sc16”)

🌌 Assign the channel(s) this streamer will receive from

stream_args.channels = [channel] # Use the channel configured earlier (e.g., 0)

try: rx_streamer = usrp.get_rx_stream(stream_args) print(“RX Streamer configured.”) except RuntimeError as e: print(f”Error configuring RX streamer: {e}”) exit()

References: 53

⚡ Prepare Buffers:

A buffer (NumPy array) is needed on the host to store the samples received in each transaction. Its size should ideally be related to the maximum number of samples the streamer can deliver in one packet to optimize efficiency. Python

🌌 --- Buffer Preparation ---

🌌 Get the maximum number of samples per packet the streamer can handle

max_samps_per_packet = rx_streamer.get_max_num_samps() print(f”Maximum samples per packet: {max_samps_per_packet}”)

🌌 Allocate the receive buffer. Size can be max_samps_per_packet or a multiple.

🌌 Using a buffer size equal to max_samps_per_packet is often efficient.

🌌 The buffer must have shape (num_channels, samps_per_buffer)

recv_buffer = np.zeros((1, max_samps_per_packet), dtype=np.complex64)

References: 59

⚡ Issue Stream Command:

To start the flow of samples from the USRP, a stream command must be issued. Python

🌌 --- Issue Stream Command ---

🌌 Create a stream command object for continuous streaming

stream_cmd = uhd.types. StreamCMD(uhd.types. StreamMode.start_cont)

🌌 Set stream_now=True to start receiving immediately

🌌 For timed reception or multi-channel sync, set stream_now=False

🌌 and provide a time_spec for a future time. [64, 66, 67]

stream_cmd.stream_now = True

🌌 Issue the command to the streamer

rx_streamer.issue_stream_cmd(stream_cmd) print(“Streaming started…”)

References: 34

⚡ Receive Loop:

The core of data acquisition is a loop that repeatedly calls the recv() method to retrieve samples and handles potential errors. Python

🌌 --- Receive Loop ---

rx_metadata = uhd.types. RXMetadata() # Metadata object to receive status

total_samps_received = 0

🌌 Example: Receive for a specific duration (e.g., 1 second)

samps_to_receive = int(sample_rate * 1.0) # Adjust duration as needed

all_samples = np.array(, dtype=np.complex64) # Accumulate samples here

🌌 Use a try/finally block to ensure the stream is stopped

try: while total_samps_received < samps_to_receive:

🌌 Call recv() on the streamer.

🌌 Provide the buffer, metadata object, and a timeout (in seconds).

🌌 Timeout prevents blocking indefinitely if no samples arrive.

num_rx_samps = rx_streamer.recv(recv_buffer, rx_metadata, timeout=0.1)

🌌 --- Error Handling ---

🌌 Check metadata for errors [64, 67, 68, 69, 70]

if rx_metadata.error_code!= uhd.types. RXMetadataErrorCode.none: error_str = rx_metadata.strerror() print(f”Receiver error: {error_str}”)

🌌 Handle specific errors

if rx_metadata.error_code == uhd.types. RXMetadataErrorCode.overflow:

🌌 Overflow (‘O’) indicates the host didn’t read samples fast enough [67, 68]

print(“O”, end=”, flush=True)

🌌 Optionally, reset counters or take other action

continue # Try receiving again

elif rx_metadata.error_code == uhd.types. RXMetadataErrorCode.timeout: print(“Timeout waiting for samples.”)

🌌 Decide whether to continue or break

break else:

🌌 Handle other potential errors (e.g., sequence errors ‘D’ on network USRPs)

print(f”Unhandled error: {error_str}”) break

🌌 --- Process Valid Samples ---

if num_rx_samps > 0:

🌌 Access the received samples from the buffer

🌌 recv_buffer has shape (num_channels, num_rx_samps)

🌌 For single channel, use recv_buffer[0, :num_rx_samps]

valid_samples = recv_buffer[0, :num_rx_samps].copy() # Copy data

🌌 Append to the main sample array

all_samples = np.concatenate((all_samples, valid_samples)) total_samps_received += num_rx_samps

🌌 *** PLACEHOLDER: Insert DSP processing here (e.g., FFT/PSD) ***

🌌 Process ‘valid_samples’ or chunks of ‘all_samples’

finally:

🌌 --- Stop Stream ---

🌌 Issue a stop command regardless of how the loop exited

stream_cmd = uhd.types. StreamCMD(uhd.types. StreamMode.stop_cont) rx_streamer.issue_stream_cmd(stream_cmd) print(“\nStreaming stopped.”) print(f”Total samples received: {total_samps_received}”)

🌌 Now ‘all_samples’ contains the received I/Q data for further processing

References: 22

⚡ Error Handling (Overflows/Dropped Packets):

A critical aspect of streaming is handling situations where the host computer cannot process incoming samples as fast as the USRP produces them.

  • Overflows (‘O’): For USB-connected devices like the B210, if the host application’s recv() call lags, the device’s internal buffers fill up. UHD detects this, prints an ‘O’ character to the console, and sets the error_code in the RXMetadata object to uhd.types. RXMetadataErrorCode.overflow.67 The stream might automatically restart once buffer space is available if in continuous mode, but data is lost during the overflow period.

  • Dropped Packets (‘D’): More common with network-connected USRPs, this indicates UDP packets were lost between the device and host, detected by sequence number gaps.23 UHD prints ‘D’ and sets the error_code accordingly.

  • Timeouts: If recv() waits longer than the specified timeout without receiving a full packet, it returns with num_rx_samps = 0 and potentially an ERROR_CODE_TIMEOUT.67

Robust applications must check rx_metadata.error_code after each recv() call and handle these conditions appropriately (e.g., log the error, potentially adjust parameters, or stop).64

⚡ Mitigation Strategies:

  • Efficient Processing: Use optimized libraries like NumPy for processing. Avoid slow Python loops on large sample arrays.

  • Reduce Sample Rate: If the host CPU is the bottleneck, lowering the sample_rate is the most direct solution.

  • Increase Buffering: In some cases, increasing UHD’s internal buffering might help absorb short processing delays. This can sometimes be influenced by device arguments passed during MultiUSRP initialization (e.g., “num_recv_frames=1000”) 12, although effectiveness varies.

  • Threading (Advanced): Move computationally intensive processing to a separate thread, allowing the main thread to focus on calling recv(). However, note that recv() itself is not thread-safe when called on the same streamer instance from multiple threads.67 Proper synchronization (e.g., using queues) is required. Reliable data acquisition hinges on a well-structured receive loop that actively manages data flow, checks for errors reported via metadata, and ensures the processing pipeline can keep pace with the configured sample rate.

3.4. Frequency Spectrum Analysis with FFT/PSD

The core analysis technique for understanding frequency content is calculating the Power Spectral Density (PSD) from the received I/Q samples.

Goal: Transform the time-domain complex I/Q samples into a frequency-domain representation showing power distribution.

The Welch method is a robust technique for estimating the PSD.71 It divides the input signal into overlapping segments, applies a window function to each segment, computes the FFT (periodogram) of each windowed segment, and then averages these periodograms.72 This averaging process significantly reduces the variance (noise) compared to computing a single FFT over the entire dataset, providing a smoother and more reliable PSD estimate.46

Python

from scipy import signal import matplotlib.pyplot as plt # For plotting example

🌌 Assume ‘all_samples’ is the numpy array (dtype=np.complex64) from the receive loop

🌌 Assume ‘sample_rate’ is the actual rate used by the USRP (in Hz)

🌌 --- PSD Calculation using Welch’s Method ---

nperseg = 1024 # Length of each segment for FFT. Powers of 2 are efficient.

noverlap = nperseg // 2 # Overlap between segments (50% is common [72])

🌌 Calculate PSD using Welch’s method

🌌 fs: Sample rate

🌌 nperseg: Segment length

🌌 noverlap: Overlap size

🌌 nfft: FFT length (usually same as nperseg unless zero-padding desired)

🌌 detrend: Remove trend from segments (False or ‘constant’ usually suitable for RF)

🌌 return_onesided: False for complex I/Q data to get full [-Fs/2, Fs/2] spectrum

🌌 scaling: ‘density’ gives units V^2/Hz (assuming input is Volts) [72, 74]

🌌 ‘spectrum’ gives units V^2 [72, 74]

🌌 average: ‘mean’ averages the periodograms

freqs, psd = signal.welch( all_samples, fs=sample_rate, nperseg=nperseg, noverlap=noverlap, nfft=nperseg, detrend=False, return_onesided=False, scaling=‘density’, average=‘mean’ )

🌌 --- Shift and Scale for Plotting/Analysis ---

🌌 The output frequencies ‘freqs’ range from 0 to Fs (or -Fs/2 to Fs/2 if two-sided was forced).

🌌 For complex data (return_onesided=False), ‘freqs’ typically goes from 0 up to Fs.

🌌 We need to shift the frequency axis and PSD values so 0 Hz (center frequency) is in the middle.

freqs_shifted = np.fft.fftshift(freqs) psd_shifted = np.fft.fftshift(psd)

🌌 Convert PSD to logarithmic scale (dB) relative to 1 V^2/Hz (or appropriate reference)

🌌 Add a small epsilon to avoid log10(0)

epsilon = 1e-20 psd_db = 10 * np.log10(psd_shifted + epsilon)

🌌 Optional: Convert to dBm/Hz (assumes 50 Ohm impedance)

🌌 Power (W) = V^2 / R. P_dBm = 10*log10(P_W / 1mW)

🌌 P_dBm/Hz = 10*log10( (V^2/Hz / 50 Ohm) / 0.001 W )

🌌 P_dBm/Hz = 10*log10(V^2/Hz) - 10*log10(50 * 0.001)

🌌 P_dBm/Hz = 10*log10(V^2/Hz) - 10*log10(0.05)

🌌 P_dBm/Hz = 10*log10(V^2/Hz) + 13.01

🌌 > ⚠️ Note: This assumes ‘all_samples’ represents voltage. Absolute power calibration is complex.

psd_dbm_hz = 10 * np.log10(psd_shifted + epsilon) + 13.01

print(f”Calculated PSD with {len(freqs)} frequency bins.”)

🌌 --- Plotting Example (See Section 4.3) ---

🌌 plt.figure(figsize=(10, 6))

🌌 plt.plot(freqs_shifted / 1e6, psd_dbm_hz) # 🌌 Plot dBm/Hz vs Relative Frequency in MHz

🌌 plt.xlabel(“Frequency Offset [MHz]”)

🌌 plt.ylabel(“PSD”)

🌌 plt.title(f”Spectrum @ {target_freq/1e9:.3f} GHz”)

🌌 plt.grid(True)

🌌 plt.show()

References: 24

⚡ Explanation of Parameters and Output:

  • nperseg: Length of FFT segments. Affects frequency resolution (longer segment = finer resolution) and variance (longer segment = more variance before averaging).

  • noverlap: Amount segments overlap. 50% is common for windows like Hann.72

  • scaling: ‘density’ (V2/Hz) is appropriate for noise measurements, while ‘spectrum’ (V2) relates more directly to power in specific bins.72

  • return_onesided=False: Crucial for complex I/Q data to analyze the full spectrum around the center frequency.

  • np.fft.fftshift: Rearranges the output of welch (and np.fft.fft) so that the zero-frequency component (corresponding to the USRP’s tuned center frequency) is in the center of the array, with negative frequency offsets to the left and positive offsets to the right.24

  • Logarithmic Units (dB): PSDs span a large dynamic range, so plotting on a logarithmic scale (dB or dBm/Hz) is standard practice for visualization.24 The conversion to dBm/Hz requires an assumption about the system impedance (typically 50 Ohms for RF) and the units of the input samples (e.g., Volts). Absolute power accuracy requires careful system calibration, which is beyond the scope of this basic guide.80

⚡ Alternative: Manual FFT/PSD Calculation

While welch is recommended, one could manually compute a periodogram from a block of N samples (iq_block):

1. Apply a window function (optional but recommended to reduce spectral leakage): windowed_block = iq_block * np.hanning(N) 2. Compute FFT: fft_result = np.fft.fft(windowed_block) 3. Shift FFT: fft_shifted = np.fft.fftshift(fft_result) 4. Compute Power: psd_manual = np.abs(fft_shifted)**2 5. Scale: This is the critical and often confusing step. For PSD in V2/Hz (‘density’), the scaling factor is approximately 1/(fs×∑wi2​), where wi​ are the window coefficients (or 1/(fs×N) for a rectangular window/no window).24 For power spectrum in V2 (‘spectrum’), scaling involves 1/(∑wi​)2 or 1/N2.78 scipy.signal.welch handles these scalings correctly based on the scaling parameter. Using established functions like scipy.signal.welch avoids potential errors in manual scaling and leverages optimized implementations, making it the preferred approach for reliable PSD estimation.

3.5. Basic RF Environment Characterization

With the PSD calculated, we can extract basic metrics to characterize the RF environment.

⚡ Estimating Average Noise Floor:

The noise floor represents the background power level inherent in the measurement system and the environment when no strong signals are present.83

  • Concept: Estimate the noise floor by averaging the PSD values across the spectrum, ideally excluding strong signal peaks.

  • Implementation:

  • A simple average (np.mean()) of the linear PSD (psd_shifted) provides a quick estimate but is sensitive to strong signals or non-flat noise floors.
  • Using the median (np.median()) is more robust to outliers (strong signals).72
  • Averaging a lower percentile (e.g., the lowest 80% of sorted PSD values) can also help exclude peaks. Python

🌌 --- Noise Floor Estimation ---

🌌 Assume ‘psd_shifted’ is the PSD array in linear units (e.g., V^2/Hz)

🌌 Simple Average (sensitive to strong signals)

avg_noise_power_linear = np.mean(psd_shifted) avg_noise_floor_dbm_hz = 10 * np.log10(avg_noise_power_linear + epsilon) + 13.01

🌌 Median (more robust to peaks)

median_noise_power_linear = np.median(psd_shifted) median_noise_floor_dbm_hz = 10 * np.log10(median_noise_power_linear + epsilon) + 13.01

🌌 Average of lowest 80% (example robust average)

sorted_psd = np.sort(psd_shifted) percentile_index = int(len(sorted_psd) * 0.80) robust_avg_noise_power = np.mean(sorted_psd[:percentile_index]) robust_avg_noise_floor_dbm_hz = 10 * np.log10(robust_avg_noise_power + epsilon) + 13.01

print(f”Estimated Average Noise Floor (Mean): {avg_noise_floor_dbm_hz:.2f} dBm/Hz”) print(f”Estimated Average Noise Floor (Median): {median_noise_floor_dbm_hz:.2f} dBm/Hz”) print(f”Estimated Average Noise Floor (Robust Avg): {robust_avg_noise_floor_dbm_hz:.2f} dBm/Hz”)

🌌 Use median or robust average for subsequent analysis if strong signals are present

estimated_noise_floor_dbm_hz = median_noise_floor_dbm_hz

References: 72

⚡ Calculating and Logging Signal Strength Metrics:

Extracting key metrics like the peak power within the observed bandwidth provides a simple measure of signal strength. Python

🌌 --- Signal Strength Metrics ---

🌌 Assume ‘freqs_shifted’ (Hz offset) and ‘psd_dbm_hz’ (dBm/Hz) are available

🌌 Find the peak power and its frequency

peak_power_dbm_hz = np.max(psd_dbm_hz) peak_freq_index = np.argmax(psd_dbm_hz) peak_freq_offset_hz = freqs_shifted[peak_freq_index]

🌌 Calculate the absolute frequency of the peak

peak_freq_actual_hz = target_freq + peak_freq_offset_hz

print(f”Peak Power Detected: {peak_power_dbm_hz:.2f} dBm/Hz”) print(f”Peak Frequency: {peak_freq_actual_hz / 1e6:.3f} MHz (Offset: {peak_freq_offset_hz / 1e6:.3f} MHz)”)

🌌 Calculate Signal-to-Noise Ratio (SNR) estimate for the peak

🌌 SNR (dB) = Peak Power (dBm/Hz) - Noise Floor (dBm/Hz)

peak_snr_db = peak_power_dbm_hz - estimated_noise_floor_dbm_hz print(f”Estimated Peak SNR: {peak_snr_db:.2f} dB”)

🌌 --- Logging (See Section 4.2 for file writing details) ---

🌌 Prepare data for logging: timestamp, center_freq, sample_rate, gain,

🌌 noise_floor, peak_power, peak_frequency, peak_snr

log_entry = { “timestamp”: datetime.now().isoformat(), “center_freq_mhz”: target_freq / 1e6, “sample_rate_mhz”: actual_rate / 1e6, “gain_db”: actual_gain, “noise_floor_dbm_hz”: estimated_noise_floor_dbm_hz, “peak_power_dbm_hz”: peak_power_dbm_hz, “peak_freq_mhz”: peak_freq_actual_hz / 1e6, “peak_snr_db”: peak_snr_db }

🌌 (Code to write log_entry to CSV would go here)

This provides basic metrics characterizing the strongest signal component relative to the noise floor within the captured bandwidth. More complex metrics, like channel power, would involve integrating the linear PSD over the frequency bins corresponding to the channel.

⚡ Identifying Potential Interference Signatures in the PSD:

Visual inspection of the plotted PSD (see Section 4.3) is crucial for identifying potential interference. Different types of interference often have characteristic shapes in the frequency domain.

⚡ Table 3: Common Interference Signatures in PSD

Interference TypeTypical PSD AppearancePotential SourcesReferences
Narrowband CarrierSharp, distinct peak(s) well above noise floorRadio transmitters (FM, TV, comms), oscillators83
Wideband Noise IncreaseElevated noise floor across a significant bandwidthDigital modulations (Wi-Fi, LTE), SMPS, faulty equipment83
Impulse/Transient NoiseBrief spikes across many frequencies (best seen on waterfall)Arcing (power lines), motors, ignition systems47
Adjacent Channel Interf.Energy spilling over from a strong nearby signalHigh-power transmitters on adjacent frequencies47
Co-Channel Interf.Multiple signals within the expected channel bandwidthMultiple transmitters on the same frequency47
Potential IMD ProductsSmaller peaks at f=2f1​−f2​ or f=2f2​−f1​ relative to strong f1​,f2​Non-linear mixing of two or more strong signals47

When analyzing the PSD plot, look for features matching these descriptions. Unexpected peaks, broadband noise elevation, or specific patterns can indicate the presence and potential type of interference. This identification is primarily qualitative in this basic application. Automated classification typically requires more sophisticated algorithms or signature databases.89



🌟 Introduction to Intermodulation Distortion (IMD):

🚀 Welcome to this comprehensive guide! This section will give you the foundational knowledge you need. IMD is a specific type of interference caused by non-linearities in RF components.80 When two or more strong signals (f1​,f2​) pass through a non-linear device (like an amplifier, mixer, or even the SDR front-end itself), new spurious signals are generated at frequencies that are mathematical combinations of the original frequencies.86

  • Third-Order IMD (IMD3): The most common and often most problematic are the third-order products, appearing at frequencies 2f1​−f2​ and 2f2​−f1​.84 These products can fall close to or within the desired signal band, causing interference, even if the original strong signals (f1​,f2​) are outside the band of interest.

  • Visual Identification: To look for potential IMD3 products in the PSD: 1. Identify two strong signals (peaks) at frequencies f1​ and f2​. 2. Calculate the expected IMD3 frequencies: fIMD3_lower​=2f1​−f2​ and fIMD3_upper​=2f2​−f1​ (assuming f2​>f1​). 3. Check the PSD plot for smaller peaks appearing at or very near these calculated frequencies.83

  • Challenges and Limitations in Simple SDR Analysis:

  • Ambiguity: A peak at an IMD3 frequency doesn’t guarantee it’s IMD. It could be another independent signal or a different type of spurious emission.91 Definitive identification often requires further tests (e.g., varying the power of f1​ or f2​ and observing how the suspected IMD peak changes).
  • SDR Self-Generated IMD: The USRP B210 itself, particularly its ADC and front-end amplifiers, has non-linearities.80 If strong signals are present at the input (approaching the ADC’s full scale or causing amplifier compression), the SDR can generate its own IMD products.88 This makes it difficult to distinguish between IMD generated externally versus internally by the measurement device itself using only the PSD. Traditional receiver metrics like third-order intercept point (IP3) may not behave linearly or predictably in SDRs, making characterization complex.88
  • Masking: IMD products are often much lower in power than the fundamental signals and can easily be below the noise floor or masked by other nearby signals, making them undetectable.94
  • Automation Complexity: Reliably automating the detection of IMD peaks (finding f1​,f2​, calculating expected frequencies, checking for peaks, and verifying relationships) while avoiding false positives is challenging in a simple scanning application. Therefore, while it’s valuable to understand IMD and visually inspect the PSD for potential candidates, this basic application cannot definitively identify or quantify IMD products. The focus should remain on identifying strong signals and general spectral characteristics.


🌟 4. Application Execution and Data Handling

This section covers running the developed Python script and handling the output data.

4.1. Running the Python Analysis Script

Assuming the Python script is saved as sdr_scan.py:

1. Open Terminal: Launch a Command Prompt or PowerShell window. 2. Activate Virtual Environment (if used): If you created a virtual environment during setup, activate it (e.g., .\venv\Scripts\activate). 3. Navigate to Script Directory: Use the cd command to navigate to the folder where sdr_scan.py is saved. 4. Execute Script: Run the script using the Python interpreter: Bash python sdr_scan.py If the script accepts command-line arguments (e.g., using argparse as shown in some examples 30), provide them as needed: Bash python sdr_scan.py —freq 4.2e9 —rate 10e6 —gain 40 —duration 5 —output-prefix scan_4p2GHz

4.2. Outputting Analysis Results

Effective output provides immediate feedback and allows for offline analysis.

⚡ Real-time Console Feedback:

Printing status information during execution is helpful for monitoring. This can include:

  • Confirmation of USRP initialization and parameters (frequency, rate, gain).

  • Start/stop messages for streaming.

  • Periodic updates of the estimated noise floor.

  • Information about detected peaks (frequency, power, SNR).

  • Error warnings, especially overflow indicators (‘O’).67

⚡ Logging to CSV File:

Storing processed data in Comma Separated Value (CSV) files provides a simple, structured format for post-analysis and visualization with external tools.99 Python’s built-in csv module is well-suited for this.99

  • Logging PSD Snapshots: Save the calculated PSD data (frequency and power values) for specific time intervals. Python import csv import time from datetime import datetime import os # Needed for checking file existence

🌌 --- Inside the processing loop, after calculating freqs_shifted, psd_dbm_hz ---

timestamp_dt = datetime.now() timestamp_str = timestamp_dt.strftime(“%Y%m%d_%H%M%S_%f”)[:-3] # YYYYMMDD_HHMMSS_ms

psd_output_filename = f”psd_snapshot_{timestamp_str}.csv”

🌌 Combine absolute frequencies (MHz) and PSD values (dBm/Hz)

abs_freqs_mhz = (target_freq + freqs_shifted) / 1e6 psd_data_to_log = np.vstack((abs_freqs_mhz, psd_dbm_hz)).T # Transpose for rows

try: with open(psd_output_filename, ‘w’, newline=”) as csvfile: # ‘w’ creates/overwrites file

csv_writer = csv.writer(csvfile)

🌌 Write header row

csv_writer.writerow()

🌌 Write data rows

csv_writer.writerows(psd_data_to_log)

🌌 print(f”Logged PSD snapshot to {psd_output_filename}”) # 🌌 Optional confirmation

except Exception as e: print(f”Error writing PSD snapshot to CSV: {e}”)

🌌 --- Logging Summary Statistics (Append to a single file) ---

summary_log_file = “analysis_summary.csv” file_exists = os.path.isfile(summary_log_file) # Check if header needs writing

🌌 Prepare data for summary log (using variables from Section 3.5)

summary_data = { ‘Timestamp’: timestamp_dt.isoformat(), ‘CenterFreq_MHz’: target_freq / 1e6, ‘SampleRate_MHz’: actual_rate / 1e6, ‘Gain_dB’: actual_gain, ‘NoiseFloor_dBm_Hz’: estimated_noise_floor_dbm_hz, ‘PeakPower_dBm_Hz’: peak_power_dbm_hz, ‘PeakFreq_MHz’: peak_freq_actual_hz / 1e6, ‘PeakSNR_dB’: peak_snr_db }

try: with open(summary_log_file, ‘a’, newline=”) as csvfile: # ‘a’ appends to file

fieldnames = list(summary_data.keys()) # Get keys for header/order

writer = csv. DictWriter(csvfile, fieldnames=fieldnames)

if not file_exists: writer.writeheader() # Write header only if file is new [100]

writer.writerow(summary_data) # Write the data row [99, 101]

🌌 print(f”Appended summary data to {summary_log_file}”) # 🌌 Optional

except Exception as e: print(f”Error writing summary log to CSV: {e}”)

References: 99 This approach uses csv.writer for the detailed PSD data and csv. DictWriter for the summary statistics, appending to the summary file over time. Using newline=” is important on Windows to prevent extra blank rows.99 For long-running captures, consider libraries like csv_logger that handle file rotation based on size or time.102

4.3. Visualizing the Data

Visualizing the spectrum is essential for interpreting the RF environment. Matplotlib provides convenient plotting capabilities directly within the Python script or for analyzing logged CSV data later.

⚡ Using Matplotlib for Basic PSD Plotting:

Python

import matplotlib.pyplot as plt import numpy as np # Assuming numpy is already imported

🌌 --- Plotting directly from calculated variables ---

🌌 Assume target_freq, freqs_shifted, psd_dbm_hz,

🌌 estimated_noise_floor_dbm_hz, peak_power_dbm_hz are available

plt.figure(figsize=(12, 7)) # Create a figure

🌌 Calculate absolute frequencies in MHz for the x-axis

abs_freqs_mhz = (target_freq + freqs_shifted) / 1e6

🌌 Plot the PSD

plt.plot(abs_freqs_mhz, psd_dbm_hz, label=‘PSD’)

🌌 Add noise floor line

plt.axhline(estimated_noise_floor_dbm_hz, color=‘red’, linestyle=’—’, label=f’Noise Floor ({estimated_noise_floor_dbm_hz:.2f} dBm/Hz)’)

🌌 Add title and labels

plt.title(f”Spectrum @ {target_freq/1e9:.3f} GHz | Rate: {actual_rate/1e6} Msps | Gain: {actual_gain} dB”) plt.xlabel(“Frequency [MHz]”) plt.ylabel(“Power Spectral Density”)

🌌 Set Y-axis limits dynamically (e.g., 10 dB below noise floor to 10 dB above peak)

plt.ylim(estimated_noise_floor_dbm_hz - 10, peak_power_dbm_hz + 10)

plt.legend() plt.grid(True)

🌌 Display the plot

plt.show()

🌌 Or, save the plot to a file

🌌 plot_filename = f”psd_plot_{timestamp_str}.png”

🌌 plt.savefig(plot_filename)

🌌 print(f”Saved PSD plot to {plot_filename}”)

🌌 plt.close() # 🌌 Close the figure if saving in a loop

References: 27

This code generates a standard PSD plot using plt.plot. Alternatively, plt.semilogy can be used if plotting linear power values.72 Matplotlib also offers a plt.psd function which combines calculation (using Welch’s method internally) and plotting, but separating calculation (scipy.signal.welch) and plotting (plt.plot) provides more flexibility for analysis and custom visualization.28

⚡ Pointers for Advanced Visualization (Waterfall/Spectrogram):

While a static PSD shows the spectrum at one point in time (or averaged over a short interval), a waterfall or spectrogram displays how the spectrum evolves over time. This is invaluable for identifying transient signals, frequency hopping patterns, or intermittent interference.91

  • Concept: Generate a 2D plot where one axis is frequency, the other is time, and the color/intensity represents the power level at that frequency and time.

  • Implementation: This typically involves: 1. Collecting I/Q data in consecutive time chunks. 2. Calculating the PSD (e.g., using scipy.signal.welch or np.fft.fft) for each chunk. 3. Stacking these 1D PSD arrays to form a 2D array. 4. Displaying the 2D array using matplotlib.pyplot.imshow 77 or matplotlib.pyplot.specgram (which performs steps 2-4 internally).27

  • External Tools: For more interactive exploration of logged data (especially large datasets), tools like Gnuplot or specialized SDR analysis software (often available for Linux, some cross-platform options exist) can be used to load and visualize the CSV files containing PSD snapshots.



🌟 5. Conclusion and Further Exploration

5.1. Summary of Application Capabilities

This guide has detailed the process of setting up a Windows development environment and creating a foundational Python application for RF analysis using an Ettus USRP B210. The developed application successfully interfaces with the hardware via the UHD API, configures key parameters like center frequency, sample rate, and gain, and streams I/Q data. It implements core spectrum analysis by calculating the Power Spectral Density using SciPy’s Welch method. Furthermore, it provides methods for estimating the average noise floor, identifying peak signal power, and offers guidance on visually recognizing common interference signatures within the PSD. Finally, methods for outputting results to the console, logging data to CSV files, and basic visualization using Matplotlib were presented.

5.2. Suggestions for Future Enhancements

The basic application serves as a starting point. Numerous enhancements can be added for more sophisticated analysis:

  • Graphical User Interface (GUI): Develop a GUI using libraries like PyQt 14 or Tkinter to provide interactive controls for parameters (frequency, rate, gain) and real-time display of the spectrum and metrics, improving usability.

  • Frequency Sweeping/Scanning: Automate the process of tuning the USRP across a wider frequency range sequentially, logging data or pausing when signals of interest are detected.

  • Advanced Interference Analysis: Implement algorithms for more specific interference detection and classification (e.g., identifying modulation types, using machine learning on spectral features 113). Automated IMD detection remains challenging but could be explored with advanced techniques, keeping limitations in mind.80

  • Channel Power Measurement: Integrate the linear PSD values over specific frequency bandwidths to calculate the power within defined channels.

  • Demodulation: Add blocks to demodulate specific signals identified during the scan (e.g., FM, AM, simple digital modulations).

  • Direction Finding: Utilize the B210’s MIMO capability (2 Rx channels) with appropriate antennas and algorithms (e.g., MUSIC, ESPRIT) to estimate the direction of arrival of signals.

  • Calibration: Implement calibration procedures using known signal sources and reference equipment to enable more accurate absolute power measurements (dBm) rather than relative power (dB) or estimated dBm/Hz based on impedance assumptions.80 This is crucial for precise characterization but requires additional hardware and expertise.

  • Multi-Band Support: Structure the code to easily switch between scanning different drone bands (900 MHz, 2.4 GHz, 5.8 GHz, 4.2 GHz) or other ranges of interest. By building upon the foundation laid out in this guide, developers can create increasingly sophisticated SDR tools tailored to specific RF analysis and interference hunting tasks on the Windows platform.

🔧 Works cited

1. USRP Hardware Driver and USRP Manual: Binary Installation, accessed on April 26, 2025, https://files.ettus.com/manual/page_install.html 2. Binary Installation - USRP Hardware Driver and USRP Manual, accessed on April 26, 2025, https://uhd.readthedocs.io/en/latest/page_install.html 3. Verifying the Operation of the USRP Using UHD and GNU Radio …, accessed on April 26, 2025, https://kb.ettus.com/Verifying_the_Operation_of_the_USRP_Using_UHD_and_GNU_Radio 4. B200/B210/B200mini/B205mini Getting Started Guides - Ettus Knowledge Base, accessed on April 26, 2025, https://kb.ettus.com/B200/B210/B200mini/B205mini_Getting_Started_Guides 5. USRP B210 & B200 Installation I Ettus USRP B210 & B200. - YouTube, accessed on April 26, 2025, https://www.youtube.com/watch?v=ObzWYGvsGmI 6. USRP Hardware Driver (UHD) installation for labAlive, accessed on April 26, 2025, https://www.etti.unibw.de/labalive/experiment/usrp/ 7. How to set up and test an Ettus USRP N200 using Windows 10 or 11 - NI Community, accessed on April 26, 2025, https://forums.ni.com/t5/USRP-Software-Radio/How-to-set-up-and-test-an-Ettus-USRP-N200-using-Windows-10-or-11/td-p/4352541 8. [USRP-users] USRP B210 and UHD and Windows, accessed on April 26, 2025, https://usrp-users.ettus.narkive.com/zruZqj0l/usrp-b210-and-uhd-and-windows 9. When trying to use my USRP in GNU Radio, I get a ” No devices found for ----->” error, accessed on April 26, 2025, https://stackoverflow.com/questions/33304828/when-trying-to-use-my-usrp-in-gnu-radio-i-get-a-no-devices-found-for 10. USRP Hardware Driver and USRP Manual: USRP2 and N2x0 Series, accessed on April 26, 2025, https://files.ettus.com/manual/page_usrp2.html 11. USRP N210 connection - Ask Ubuntu, accessed on April 26, 2025, https://askubuntu.com/questions/1013916/usrp-n210-connection 12. USRP in Python | PySDR: A Guide to SDR and DSP using Python, accessed on April 26, 2025, https://pysdr.org/content/usrp.html 13. Soapysdr | Anaconda.org, accessed on April 26, 2025, https://anaconda.org/conda-forge/soapysdr 14. Installation — Soapy v0.13.1-214-g093ce9c-dirty documentation, accessed on April 26, 2025, https://soapy.readthedocs.io/en/latest/install.html 15. USRP Hardware Driver and USRP Manual: Python API, accessed on April 26, 2025, https://files.ettus.com/manual/page_python.html 16. Source build failure for UHD 4.5.0.0 on Windows when enabling Python API #709 - GitHub, accessed on April 26, 2025, https://github.com/EttusResearch/uhd/issues/709 17. Python vs C++: Detailed Comparison of Technical Differences and Use Cases - llego.dev, accessed on April 26, 2025, https://llego.dev/posts/python-vs-c-plus-plus-comparison/ 18. Best Programming language to control SDRs : r/RTLSDR - Reddit, accessed on April 26, 2025, https://www.reddit.com/r/RTLSDR/comments/1ga4zj0/best_programming_language_to_control_sdrs/ 19. UHD Python API - Ettus Knowledge Base, accessed on April 26, 2025, https://kb.ettus.com/index.php?title=UHD_Python_API&action=pdfbook&format=single 20. Introduction | PySDR: A Guide to SDR and DSP using Python, accessed on April 26, 2025, https://pysdr.org/content/intro.html 21. Python API - UHD and USRP Manual, accessed on April 26, 2025, https://files.ettus.com/manual_archive/v3.13.1.0/html/page_python.html 22. Python API - USRP Hardware Driver and USRP Manual - Read the Docs, accessed on April 26, 2025, https://uhd.readthedocs.io/en/latest/page_python.html 23. Overflow Error with UHD Python - API · Issue #121 · EttusResearch/uhd - GitHub, accessed on April 26, 2025, https://github.com/EttusResearch/uhd/issues/121 24. IQ Sampling | PySDR: A Guide to SDR and DSP using Python, accessed on April 26, 2025, https://pysdr.org/content/sampling.html 25. IQ Plot - Python - Amarisoft Tech Academy, accessed on April 26, 2025, https://tech-academy.amarisoft.com/IQ_Plot.html 26. Frequency Domain | PySDR: A Guide to SDR and DSP using Python, accessed on April 26, 2025, https://pysdr.org/content/frequency_domain.html 27. Lab 1: Working with IQ data in Python - WITest, accessed on April 26, 2025, http://witestlab.poly.edu/~ffund/el9043/labs/lab1 28. Matplotlib PSD Plotting Tutorial - LabEx, accessed on April 26, 2025, https://labex.io/tutorials/python-matplotlib-psd-plotting-48884 29. Matplotlib.pyplot.psd() in Python - GeeksforGeeks, accessed on April 26, 2025, https://www.geeksforgeeks.org/matplotlib-pyplot-psd-in-python/ 30. UHD Python API - Ettus Knowledge Base, accessed on April 26, 2025, https://kb.ettus.com/UHD_Python_API 31. FAQ - Deepwave Digital Docs, accessed on April 26, 2025, https://docs.deepwavedigital.com/faq/ 32. Evaluation of SDR Boards and Toolchains - Klofas.com, accessed on April 26, 2025, https://www.klofas.com/blog/2020/satnogs-station-and-minicircuits-lna-modifications/Evaluation_of_SDR_Boards-1.0.pdf 33. SimpleSoapy - PyPI, accessed on April 26, 2025, https://pypi.org/project/SimpleSoapy/ 34. uhd/host/examples/python/rx_to_file.py at master - GitHub, accessed on April 26, 2025, https://github.com/EttusResearch/uhd/blob/master/host/examples/python/rx_to_file.py 35. RTL-SDR in Python | PySDR: A Guide to SDR and DSP using Python, accessed on April 26, 2025, https://pysdr.org/content/rtlsdr.html 36. PySDR: A Guide to SDR and DSP using Python, accessed on April 26, 2025, https://pysdr.org/ 37. Building and Installing the USRP Open Source Toolchain (UHD and GNU Radio) on Windows - Ettus Knowledge Base, accessed on April 26, 2025, https://kb.ettus.com/Building_and_Installing_the_USRP_Open_Source_Toolchain_(UHD_and_GNU_Radio)_on_Windows 38. USRP Hardware Driver and USRP Manual: Building and Installing UHD from source, accessed on April 26, 2025, https://files.ettus.com/manual/page_build_guide.html 39. uhd/host/docs/build.rst at master - GitHub, accessed on April 26, 2025, https://github.com/GREO/uhd/blob/master/host/docs/build.rst 40. Install C and C++ support in Visual Studio - Learn Microsoft, accessed on April 26, 2025, https://learn.microsoft.com/en-us/cpp/build/vscpp-step-0-installation?view=msvc-170 41. New Software for SDR : r/RTLSDR - Reddit, accessed on April 26, 2025, https://www.reddit.com/r/RTLSDR/comments/lewgwm/new_software_for_sdr/ 42. Installing the UHD Python/C++ APIs for USRP : r/DSP - Reddit, accessed on April 26, 2025, https://www.reddit.com/r/DSP/comments/1enwdhs/installing_the_uhd_pythonc_apis_for_usrp/ 43. FAQ - GNU Radio Wiki, accessed on April 26, 2025, https://wiki.gnuradio.org/index.php/FAQ 44. Building and Installing UHD from source, accessed on April 26, 2025, https://uhd.readthedocs.io/en/uhd-4.5/page_build_guide.html 45. What is power spectral density (PSD), and why is it important? - Liquid Instruments, accessed on April 26, 2025, https://liquidinstruments.com/blog/what-is-power-spectral-density-and-why-is-it-important/ 46. Spectrum Analysis in Python | GeeksforGeeks, accessed on April 26, 2025, https://www.geeksforgeeks.org/spectrum-analysis-in-python/ 47. Interference Hunting Application Note - How To | Tektronix, accessed on April 26, 2025, https://www.tek.com/en/documents/application-note/interference-hunting-application-note 48. USRP Hardware Driver and USRP Manual: uhd::usrp::multi_usrp Class Reference, accessed on April 26, 2025, https://files.ettus.com/manual/classuhd_1_1usrp_1_1multi__usrp.html 49. Addressing multiple USRPs with the UHD Python API - Stack Overflow, accessed on April 26, 2025, https://stackoverflow.com/questions/78522393/addressing-multiple-usrps-with-the-uhd-python-api 50. Tutorial: Practical Use of SDR for Machine Learning in RF Environments ACM-SE 2022, accessed on April 26, 2025, https://acmse.net/2022/wp-content/slides/ACMSE2022-Tutorial1-Slides-Neel.pdf 51. Using the UHD API for B210 - c++ - Stack Overflow, accessed on April 26, 2025, https://stackoverflow.com/questions/77056116/using-the-uhd-api-for-b210 52. USRP Hardware Driver and USRP Manual: USRP B2x0 Series, accessed on April 26, 2025, https://files.ettus.com/manual/page_usrp_b200.html 53. Choosing USRP antenna at runtime - c++ - Stack Overflow, accessed on April 26, 2025, https://stackoverflow.com/questions/74621075/choosing-usrp-antenna-at-runtime 54. uhd/host/examples/python/curses_fft.py at master - GitHub, accessed on April 26, 2025, https://github.com/EttusResearch/uhd/blob/master/host/examples/python/curses_fft.py 55. uhd/host/python/uhd/usrp/multi_usrp.py at master · EttusResearch/uhd - GitHub, accessed on April 26, 2025, https://github.com/EttusResearch/uhd/blob/master/host/python/uhd/usrp/multi_usrp.py 56. ZBX Daughterboard - USRP Hardware Driver and USRP Manual, accessed on April 26, 2025, https://uhd.readthedocs.io/en/latest/page_zbx.html 57. Using 2 transmit or 2 receive antennas simultaneously - Orbit, accessed on April 26, 2025, https://www.orbit-lab.org/wiki/Tutorials/k0SDR/Tutorial22 58. Using the UHD Python API to control USRPs: Spectrum analyzer example - Radio Science, accessed on April 26, 2025, http://www.radio-science.net/2020/05/using-uhd-python-api.html 59. 6. USRP en Python | PySDR: A Guide to SDR and DSP using Python, accessed on April 26, 2025, https://pysdr.org/es/content-es/usrp.html 60. How Can I generate detect signals (2.4GHz) and generate spectrograms from them like this one? - Stack Overflow, accessed on April 26, 2025, https://stackoverflow.com/questions/78076552/how-can-i-generate-detect-signals-2-4ghz-and-generate-spectrograms-from-them-l 61. USRP sc16, sc8 CPU format in Python API · Issue #21 · 777arc/PySDR - GitHub, accessed on April 26, 2025, https://github.com/777arc/PySDR/issues/21 62. How to set USRP antenna in GNU-radio - Stack Overflow, accessed on April 26, 2025, https://stackoverflow.com/questions/63907122/how-to-set-usrp-antenna-in-gnu-radio 63. Device streaming - UHD and USRP Manual, accessed on April 26, 2025, https://files.ettus.com/manual/page_stream.html 64. uhd/host/examples/python/benchmark_rate.py at master - GitHub, accessed on April 26, 2025, https://github.com/EttusResearch/uhd/blob/master/host/examples/python/benchmark_rate.py 65. X310 USRP with python - Stack Overflow, accessed on April 26, 2025, https://stackoverflow.com/questions/74614050/x310-usrp-with-python 66. Python API: cannot stream on two channels · Issue #325 · EttusResearch/uhd - GitHub, accessed on April 26, 2025, https://github.com/EttusResearch/uhd/issues/325 67. uhd::rx_streamer Class Reference - USRP Hardware Driver and USRP Manual, accessed on April 26, 2025, https://uhd.readthedocs.io/en/latest/classuhd_1_1rx__streamer.html 68. USRP Hardware Driver and USRP Manual: General Application Notes, accessed on April 26, 2025, https://files.ettus.com/manual/page_general.html 69. [USRP-users] receving less samples than requested but error_code is 0, accessed on April 26, 2025, https://usrp-users.ettus.narkive.com/MBiZdICF/receving-less-samples-than-requested-but-error-code-is-0 70. How to stream a fixed number of samples from a USRP using UHD - Stack Overflow, accessed on April 26, 2025, https://stackoverflow.com/questions/63459644/how-to-stream-a-fixed-number-of-samples-from-a-usrp-using-uhd 71. computing the averge power spectral density with python, accessed on April 26, 2025, https://dsp.stackexchange.com/questions/93418/computing-the-averge-power-spectral-density-with-python 72. welch — SciPy v1.15.2 Manual, accessed on April 26, 2025, https://docs.scipy.org/doc/scipy/reference/generated/scipy.signal.welch.html 73. Estimating the Power Spectral Density of Ambient Seismic Noise - Geophydog, accessed on April 26, 2025, https://geophydog.cool/post/psd_notes/ 74. periodogram — SciPy v1.15.2 Manual, accessed on April 26, 2025, https://docs.scipy.org/doc/scipy/reference/generated/scipy.signal.periodogram.html 75. Sample rate vs noise floor in power spectral density measurement, accessed on April 26, 2025, https://dsp.stackexchange.com/questions/78894/sample-rate-vs-noise-floor-in-power-spectral-density-measurement 76. Plotting power spectrum in python - numpy - Stack Overflow, accessed on April 26, 2025, https://stackoverflow.com/questions/15382076/plotting-power-spectrum-in-python 77. 1.5.12.9. Spectrogram, power spectral density - Scientific Python Lectures, accessed on April 26, 2025, https://lectures.scientific-python.org/intro/scipy/auto_examples/plot_spectrogram.html 78. PSD calculation from FFT does not match with the value from scipy.signal.welch, accessed on April 26, 2025, https://dsp.stackexchange.com/questions/94571/psd-calculation-from-fft-does-not-match-with-the-value-from-scipy-signal-welch 79. 1.6.12.9. Spectrogram, power spectral density — Scipy lecture notes, accessed on April 26, 2025, https://scipy-lectures.org/intro/scipy/auto_examples/plot_spectrogram.html 80. Challenges and Solutions in using SDRs for Test and Measurement Applications, accessed on April 26, 2025, https://www.everythingrf.com/community/challenges-and-solutions-in-using-sdrs-for-test-and-measurement-applications 81. PSD of signal in numpy and how to scale it - Stack Overflow, accessed on April 26, 2025, https://stackoverflow.com/questions/23593295/psd-of-signal-in-numpy-and-how-to-scale-it 82. Prefactors computing PSD of a signal with numpy.fft VS. scipy.signal.welch, accessed on April 26, 2025, https://dsp.stackexchange.com/questions/60554/prefactors-computing-psd-of-a-signal-with-numpy-fft-vs-scipy-signal-welch 83. Spectrum Analysis for Wireless Mics and IEMs: Part 3 - RF Venue, accessed on April 26, 2025, https://www.rfvenue.com/blog/spectrum-anaylsis-part-3-interference 84. Identifying and Locating Radio Frequency Interference (RFI), accessed on April 26, 2025, https://interferencetechnology.com/identifying-and-locating-radio-frequency-interference-rfi/ 85. Interference Hunting –– - Tektronix, accessed on April 26, 2025, https://download.tek.com/document/37W_60877_0_Interference_AN%20.pdf 86. Intermodulation Distortion Measurements of Ferrites Application Note, document #201537 - Richardson RFPD, accessed on April 26, 2025, https://shop.richardsonrfpd.com/docs/rfpd/Skyworks_Intermodulation_Distortion_Measurements_of_Ferrites.pdf 87. Application Note - Intermodulation Distortion Measurements on Modern Spectrum Analyzers - GLORIS Rohde & Schwarz, accessed on April 26, 2025, https://scdn.rohde-schwarz.com/ur/pws/dl_downloads/dl_application/application_notes/1ef79/1EF79_1E.pdf 88. Performance testing of Software Defined Radios By Andrew Barron ZL3DW October 2013 Do SDRs perform better than conventional Sup - QSL.net, accessed on April 26, 2025, https://www.qsl.net/ab4oj/sdr/sdr_zl3dw.pdf 89. Device Identification with a Spectrum Analyzer - CWNP, accessed on April 26, 2025, https://www.cwnp.com/device-identification-with-a-spectrum-analyzer/ 90. Real world vs ideal signals in SDR-based simulations - Embedded, accessed on April 26, 2025, https://www.embedded.com/real-world-vs-ideal-signals-in-sdr-based-simulations/ 91. IntermodAnalyzer — RF Spectrum Analyzer & Frequency Coordination Software for RTLSDR (RTL2832U) - Nuts About Nets, accessed on April 26, 2025, https://nutsaboutnets.com/intermodanalyzer/ 92. S93087B Intermodulation Distortion Measurements - Keysight, accessed on April 26, 2025, https://www.keysight.com/us/en/product/S93087B/intermodulation-distortion-measurements.html 93. ClearWaves — RF Spectrum Analyzer & Frequency Coordination Software - RF Explorer, accessed on April 26, 2025, https://rfexplorer.com/clearwaves/ 94. HF Receiver Testing, accessed on April 26, 2025, https://files.tapr.org/meetings/DCC_2015/DCC2015-HF-Receiver-Performance-Specs-VA7OJ.pdf 95. HOW IMPORTANT ARE RECEIVER PERFORMANCE CRITERIA IN AN ERA OF SOFTWARE DEFINED RADIOS? - AB4OJ/VA7OJ’s Home Page, accessed on April 26, 2025, https://www.ab4oj.com/sdr/seapac17/sdrpres14.pdf 96. IMD in Digital Receivers - SM5BSZ, accessed on April 26, 2025, https://www.sm5bsz.com/dynrange/qex/digital-imd.pdf 97. HF Receiver Testing, accessed on April 26, 2025, https://files.tapr.org/meetings/DCC_2015/DCC2015-HF-Receiver-Performance-Specs-VA7OJ-1.pdf 98. Why THD+N, SINAD and IMD suck and explain what we hear poorly. - Reddit, accessed on April 26, 2025, https://www.reddit.com/r/headphones/comments/ltwyz9/why_thdn_sinad_and_imd_suck_and_explain_what_we/ 99. Python CSV File: Read and Write CSV File with Example - WsCube Tech, accessed on April 26, 2025, https://www.wscubetech.com/resources/python/csv 100. csv — CSV File Reading and Writing — Python 3.13.3 documentation, accessed on April 26, 2025, https://docs.python.org/3/library/csv.html 101. Writing CSV files in Python | GeeksforGeeks, accessed on April 26, 2025, https://www.geeksforgeeks.org/writing-csv-files-in-python/ 102. csv-logger - PyPI, accessed on April 26, 2025, https://pypi.org/project/csv-logger/ 103. How to extract information from log file in python? | Sololearn: Learn to code for FREE!, accessed on April 26, 2025, https://www.sololearn.com/en/Discuss/1104270/how-to-extract-information-from-log-file-in-python 104. convert log to csv file : r/learnpython - Reddit, accessed on April 26, 2025, https://www.reddit.com/r/learnpython/comments/sckq5p/convert_log_to_csv_file/ 105. code help to convert log file to csv format - Python Forum, accessed on April 26, 2025, https://python-forum.io/thread-15500.html 106. How to Compute FFT and Plot Frequency Spectrum in Python using Numpy and Matplotlib, accessed on April 26, 2025, https://m.youtube.com/watch?v=O0Y8FChBaFU 107. Power spectral density (PSD) — Matplotlib 3.10.1 documentation, accessed on April 26, 2025, https://matplotlib.org/stable/gallery/lines_bars_and_markers/psd_demo.html 108. python - How to make a PSD plot using np.fft.fft? - Stack Overflow, accessed on April 26, 2025, https://stackoverflow.com/questions/49887970/how-to-make-a-psd-plot-using-np-fft-fft 109. matplotlib.pyplot.psd — Matplotlib 3.1.2 documentation, accessed on April 26, 2025, https://matplotlib.org/3.1.1/api/_as_gen/matplotlib.pyplot.psd.html 110. Plot the power spectral density using Matplotlib – Python - GeeksforGeeks, accessed on April 26, 2025, https://www.geeksforgeeks.org/plot-the-power-spectral-density-using-matplotlib-python/ 111. How to properly calculate PSD plot (Power Spectrum Density Plot) for images in order to remove periodic noise? - Stack Overflow, accessed on April 26, 2025, https://stackoverflow.com/questions/78642079/how-to-properly-calculate-psd-plot-power-spectrum-density-plot-for-images-in-o 112. Plotting Power Spectral Density in Matplotlib - Tutorialspoint, accessed on April 26, 2025, https://www.tutorialspoint.com/plotting-power-spectral-density-in-matplotlib 113. UHD for Pythonistas - GNU Radio, accessed on April 26, 2025, https://www.gnuradio.org/grcon/grcon18/presentations/UHD_for_Pythonistas/