🌌 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
Parameter | Python UHD API Call | B210 Notes / Example Value | References |
---|---|---|---|
Center Frequency | usrp.set_rx_freq(tune_request, chan) | 70 MHz - 6 GHz.52 tune_request = uhd.types. TuneRequest(4.2e9) | 12 |
Sample Rate | usrp.set_rx_rate(rate, chan) | Up to ~61 Msps 50, often USB limited. rate = 10e6. May be coerced.23 | 12 |
RF Gain | usrp.set_rx_gain(gain, chan) | 0 - 76 dB.12 gain = 40. | 12 |
Antenna Selection | usrp.set_rx_antenna(antenna_name, chan) | ‘TX/RX’, ‘RX2’ for each channel (A/B).52 antenna = “RX2”. | 48 |
Master Clock Rate | usrp.set_master_clock_rate(rate) | Default usually sufficient. rate = 20e6 (Example). 52 | 52 |
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.
⚡ Recommended Method: scipy.signal.welch
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 Type | Typical PSD Appearance | Potential Sources | References |
---|---|---|---|
Narrowband Carrier | Sharp, distinct peak(s) well above noise floor | Radio transmitters (FM, TV, comms), oscillators | 83 |
Wideband Noise Increase | Elevated noise floor across a significant bandwidth | Digital modulations (Wi-Fi, LTE), SMPS, faulty equipment | 83 |
Impulse/Transient Noise | Brief spikes across many frequencies (best seen on waterfall) | Arcing (power lines), motors, ignition systems | 47 |
Adjacent Channel Interf. | Energy spilling over from a strong nearby signal | High-power transmitters on adjacent frequencies | 47 |
Co-Channel Interf. | Multiple signals within the expected channel bandwidth | Multiple transmitters on the same frequency | 47 |
Potential IMD Products | Smaller peaks at f=2f1−f2 or f=2f2−f1 relative to strong f1,f2 | Non-linear mixing of two or more strong signals | 47 |
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/