### Install and Run Phaser SD Card Setup Script Source: https://pysdr.org/content/phaser This script downloads, makes executable, and runs the phaser_sdcard_setup.sh script. It is followed by a reboot and a call to raspi-config for system configuration. Assumes wget is available and the script is downloaded from the specified GitHub URL. ```bash wget https://github.com/mthoren-adi/rpi_setup_stuff/raw/main/phaser/phaser_sdcard_setup.sh sudo chmod +x phaser_sdcard_setup.sh ./phaser_sdcard_setup.sh sudo reboot sudo raspi-config ``` -------------------------------- ### Run Phaser GUI Example Application Source: https://pysdr.org/content/phaser This command launches the pre-built example application for the Phaser. It requires the HB100 frequency and calibration files to be generated. The GUI allows for visualization of data and includes an 'Auto Refresh Data' option. ```python python phaser_gui.py ``` -------------------------------- ### Initialize and Run PyQt Application Source: https://pysdr.org/content/pyqt Initializes the QApplication, creates and shows the main window, sets up signal handling for graceful exit on SIGINT, and starts the Qt event loop. ```python app = QApplication([]) window = MainWindow() window.show() # Windows are hidden by default signal.signal(signal.SIGINT, signal.SIG_DFL) # this lets control-C actually close the app app.exec() # Start the event loop ``` -------------------------------- ### Test UHD Drivers and Python API Source: https://pysdr.org/content/usrp Verifies the installation of the UHD drivers and Python API by importing the 'uhd' library and creating a MultiUSRP object. It then attempts to receive a small number of samples and prints the first few. Successful execution without errors indicates a correct setup. ```python import uhd usrp = uhd.usrp.MultiUSRP() samples = usrp.recv_num_samps(10000, 100e6, 1e6, [0], 50) print(samples[0:10]) ``` -------------------------------- ### Install UHD and Python API using apt and pip Source: https://pysdr.org/content/usrp Installs the UHD library and its Python API from source. This process involves updating package lists, installing dependencies, cloning the UHD repository, configuring the build with CMake, compiling, and installing. It requires root privileges for installation and system configuration. ```bash sudo apt update sudo apt install git cmake libboost-all-dev libusb-1.0-0-dev build-essential sudo pip install pybind11[global] pip install numpy==1.26.4 docutils mako requests ruamel.yaml setuptools cd ~ git clone https://github.com/EttusResearch/uhd.git cd uhd git checkout v4.8.0.0 cd host mkdir build cd buildcmake -DENABLE_TESTS=OFF -DENABLE_C_API=OFF -DENABLE_PYTHON_API=ON -DENABLE_MANUAL=OFF .. make -j8 sudo make install sudo ldconfig ``` -------------------------------- ### Install HackRF Library and Host Tools (Bash) Source: https://pysdr.org/content/hackrf Clones the HackRF repository, checks out a specific commit, builds and installs the host library and tools. This process requires git, cmake, and make. It concludes with updating the system's dynamic linker configuration and copying HackRF utilities to the system's PATH. ```bash git clone https://github.com/greatscottgadgets/hackrf.git cd hackrf git checkout 17f3943 cd host mkdir build cd build cmake .. make sudo make install sudo ldconfig sudo cp /usr/local/bin/hackrf* /usr/bin/. ``` -------------------------------- ### Enable udev Service in WSL Source: https://pysdr.org/content/rtlsdr Configures WSL to start the udev service on boot, which is necessary for udev rules to function correctly within the WSL environment. Requires restarting WSL after modification. ```bash [boot] command="service udev start" ``` -------------------------------- ### Basic PyQt Application Layout in Python Source: https://pysdr.org/index Demonstrates the fundamental structure of a PyQt application, including setting up the main window and basic layout management. This serves as a starting point for building GUIs. ```python import sys from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QLabel app = QApplication(sys.argv) window = QWidget() window.setWindowTitle('Basic PyQt App') layout = QVBoxLayout() label = QLabel('Hello, PyQt!') layout.addWidget(label) window.setLayout(layout) window.show() sys.exit(app.exec_()) ``` -------------------------------- ### Create SigMF Metadata File (JSON) Source: https://pysdr.org/content/iq_files An example of a minimal SigMF metadata file in JSON format. This file describes the global parameters of an IQ recording, including datatype, sample rate, hardware used, author, and version. It also defines a capture with its start time and frequency, and includes an empty annotations list. ```json { "global": { "core:datatype": "cf32_le", "core:sample_rate": 1000000, "core:hw": "PlutoSDR with 915 MHz whip antenna", "core:author": "Art Vandelay", "core:version": "1.0.0" }, "captures": [ { "core:sample_start": 0, "core:frequency": 915000000 } ], "annotations": [] } ``` -------------------------------- ### Receive Samples using recv_num_samps() Source: https://pysdr.org/content/usrp Demonstrates receiving a specified number of samples from a USRP device using the `recv_num_samps()` convenience function. This function simplifies the process by handling stream setup and teardown internally. It allows tuning to a specific frequency, setting a sample rate, and specifying gain. The example shows how to tune to 100 MHz with a 1 MHz sample rate and 50 dB gain, then prints the first 10 received samples. ```python import uhd usrp = uhd.usrp.MultiUSRP() samples = usrp.recv_num_samps(10000, 100e6, 1e6, [0], 50) # units: N, Hz, Hz, list of channel IDs, dB print(samples[0:10]) ``` -------------------------------- ### Install bladeRF Software on Ubuntu/WSL Source: https://pysdr.org/content/bladerf Installs the bladeRF software suite, including the core library, Python bindings, command-line tools, and firmware/FPGA downloaders, on Debian-based systems like Ubuntu. This process involves updating package lists, installing dependencies, cloning the repository, compiling from source, and installing the Python bindings. ```bash sudo apt update sudo apt install cmake python3-pip libusb-1.0-0 cd ~ git clone --depth 1 https://github.com/Nuand/bladeRF.git cd bladeRF/host mkdir build && cd build cmake .. make -j8 sudo make install sudo ldconfig cd ../libraries/libbladeRF_bindings/python sudo python3 setup.py install ``` -------------------------------- ### Main Window Setup and Layout in Python Source: https://pysdr.org/content/pyqt Defines the main application window, sets its title and size, and initializes the layout. It also sets up the SDR worker thread and its connection to the main thread. ```python class MainWindow(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("The PySDR Spectrum Analyzer") self.setFixedSize(QSize(1500, 1000)) # window size, starting size should fit on 1920 x 1080 self.spectrogram_min = 0 self.spectrogram_max = 0 layout = QGridLayout() # overall layout # Initialize worker and thread self.sdr_thread = QThread() self.sdr_thread.setObjectName('SDR_Thread') # so we can see it in htop, note you have to hit F2 -> Display options -> Show custom thread names worker = SDRWorker() worker.moveToThread(self.sdr_thread) ``` -------------------------------- ### Install HackRF Python API (Bash) Source: https://pysdr.org/content/hackrf Installs the necessary development library (`libusb-1.0-0-dev`) and the HackRF Python bindings (`python-hackrf`). This is a prerequisite for using the HackRF library with Python. The command uses `apt` for package management and `pip` for Python package installation. ```bash sudo apt install libusb-1.0-0-dev pip install python_hackrf==1.2.7 ``` -------------------------------- ### Install PyRTLSDR Python Wrapper Source: https://pysdr.org/content/rtlsdr Installs the Python wrapper for the librtlsdr library, enabling programmatic control of the RTL-SDR device from Python scripts. Requires pip to be installed. ```bash sudo pip install pyrtlsdr ``` -------------------------------- ### Install RTL-SDR Software on Ubuntu Source: https://pysdr.org/content/rtlsdr Installs the librtlsdr library and command-line tools like rtl_sdr, rtl_tcp, rtl_fm, and rtl_test on Debian-based systems. This is a prerequisite for using the RTL-SDR hardware. ```bash sudo apt install rtl-sdr ``` -------------------------------- ### Create Minimal Qt Application with PyQt6 Source: https://pysdr.org/content/pyqt Demonstrates how to create a basic Qt application using PyQt6, featuring a main window with a single push button. The button's 'clicked' signal is connected to a callback function that prints to the console. Requires 'PyQt6' to be installed. ```python from PyQt6.QtWidgets import QApplication, QMainWindow, QPushButton # Subclass QMainWindow to customize your application's main window class MainWindow(QMainWindow): def __init__(self): super().__init__() # Example GUI component example_button = QPushButton('Push Me') def on_button_click(): print("beep") example_button.clicked.connect(on_button_click) self.setCentralWidget(example_button) app = QApplication([]) window = MainWindow() window.show() # Windows are hidden by default app.exec() # Start the event loop ``` -------------------------------- ### Initialize Costas Loop Variables (Python) Source: https://pysdr.org/content/sync Initializes variables for the Costas Loop, a PLL-based technique for fine frequency and phase offset correction in digital signals. This setup is part of a feedback loop designed to continuously track and minimize frequency offsets. ```python N = len(samples) phase = 0 freq = 0 ``` -------------------------------- ### Python Signal Simulation Setup Source: https://pysdr.org/content/doa Sets up parameters for simulating a received signal at an antenna array. This includes defining the sampling rate and the number of samples to generate for the simulation. These parameters are crucial for creating realistic signal data. ```python import numpy as np import matplotlib.pyplot as plt sample_rate = 1e6 N = 10000 # number of samples to simulate ``` -------------------------------- ### Synchronize USRP to External Clock and PPS Source: https://pysdr.org/nl/content-nl/usrp This code configures the USRP to synchronize its clock and timing to external 10 MHz and PPS (Pulse Per Second) signals. This is crucial for multi-receiver applications like TDOA. It also includes an example of scheduling a receive stream to start precisely on the next PPS signal. ```python # Synchronize to external clock and PPS usrp.set_clock_source("external") usrp.set_time_source("external") # Wait for 1 PPS to happen, then set the time at next PPS to 0.0 time_at_last_pps = usrp.get_time_last_pps().get_real_secs() while time_at_last_pps == usrp.get_time_last_pps().get_real_secs(): time.sleep(0.1) # keep waiting till it happens- if this while loop never finishes then the PPS signal isn't there usrp.set_time_next_pps(uhd.libpyuhd.types.time_spec(0.0)) # Schedule Rx of num_samps samples exactly 3 seconds from last PPS stream_cmd = uhd.types.StreamCMD(uhd.types.StreamMode.num_done) stream_cmd.num_samps = num_samps stream_cmd.stream_now = False stream_cmd.time_spec = uhd.libpyuhd.types.time_spec(3.0) # set start time (try tweaking this) streamer.issue_stream_cmd(stream_cmd) # Receive Samples. recv() will return zeros, then our samples, then more zeros, letting us know it's done waiting_to_start = True # keep track of where we are in the cycle (see above comment) nsamps = 0 i = 0 samples = np.zeros(num_samps, dtype=np.complex64) while nsamps != 0 or waiting_to_start: nsamps = streamer.recv(recv_buffer, metadata) if nsamps and waiting_to_start: waiting_to_start = False elif nsamps: samples[i:i+nsamps] = recv_buffer[0][0:nsamps] i += nsamps ``` -------------------------------- ### LMS Beamformer Simulation Setup - Python Source: https://pysdr.org/content/doa Sets up the simulation parameters, including sample rate, array element spacing, number of samples, number of elements, angles of arrival for the SOI and interferers, and time vector. It also defines the steering vectors for the SOI and interferers. ```python # Scenario sample_rate = 1e6 d = 0.5 # half wavelength spacing N = 100000 # number of samples to simulate Nr = 8 # elements theta_soi = 20 / 180 * np.pi # convert to radians theta2 = 60 / 180 * np.pi theta3 = -50 / 180 * np.pi t = np.arange(N)/sample_rate # time vector s1 = np.exp(2j * np.pi * d * np.arange(Nr) * np.sin(theta_soi)).reshape(-1,1) # 8x1 s2 = np.exp(2j * np.pi * d * np.arange(Nr) * np.sin(theta2)).reshape(-1,1) s3 = np.exp(2j * np.pi * d * np.arange(Nr) * np.sin(theta3)).reshape(-1,1) ``` -------------------------------- ### Define ULA Element Spacing in Python Source: https://pysdr.org/content/2d_beamforming Defines parameters for a Uniform Linear Array (ULA) including the number of elements, carrier frequency, wavelength, and element spacing. This setup is used to demonstrate the generalized steering vector equation and connect it to previous ULA concepts. The element spacing is set relative to the wavelength. ```python Nr = 4 fc = 5e9 wavelength = 3e8 / fc d = 0.5 * wavelength # in meters ``` -------------------------------- ### Initialize and Configure HackRF with Python API (Python) Source: https://pysdr.org/content/hackrf Initializes the HackRF library, opens a connection to the device, and configures basic parameters such as sample rate, frequency, and gain settings. This script serves as a basic test to ensure the Python API is correctly installed and communicating with the HackRF device. It does not produce output if successful. ```python from python_hackrf import pyhackrf # type: ignore pyhackrf.pyhackrf_init() sdr = pyhackrf.pyhackrf_open() sdr.pyhackrf_set_sample_rate(10e6) sdr.pyhackrf_set_antenna_enable(False) sdr.pyhackrf_set_freq(100e6) sdr.pyhackrf_set_amp_enable(False) sdr.pyhackrf_set_lna_gain(30) # LNA gain - 0 to 40 dB in 8 dB steps sdr.pyhackrf_set_vga_gain(50) # VGA gain - 0 to 62 dB in 2 dB steps sdr.pyhackrf_close() ``` -------------------------------- ### Create and Configure Sample Rate Dropdown with Label (Python) Source: https://pysdr.org/content/pyqt This snippet shows the creation of a QComboBox (dropdown) for selecting the sample rate. It populates the dropdown with predefined sample rates, sets the initial selection, and connects its change signal to update both a worker thread and a QLabel. The QLabel displays the selected sample rate in MHz. The `addItems` method takes a list of strings, and `setCurrentIndex` sets the initial selection by index. ```python # Sample rate dropdown using QComboBox sample_rate_combobox = QComboBox() sample_rate_combobox.addItems([str(x) + ' MHz' for x in sample_rates]) sample_rate_combobox.setCurrentIndex(0) # must give it the index, not string sample_rate_combobox.currentIndexChanged.connect(worker.update_sample_rate) sample_rate_label = QLabel() def update_sample_rate_label(val): sample_rate_label.setText("Sample Rate: " + str(sample_rates[val]) + " MHz") sample_rate_combobox.currentIndexChanged.connect(update_sample_rate_label) update_sample_rate_label(sample_rate_combobox.currentIndex()) # initialize the label layout.addWidget(sample_rate_combobox, 6, 0) layout.addWidget(sample_rate_label, 6, 1) ``` -------------------------------- ### Install PlutoSDR Drivers and Python API (Ubuntu) Source: https://pysdr.org/content/pluto Installs libiio, libad9361-iio, and pyadi-iio on Ubuntu 22. These are essential libraries for interfacing with the PlutoSDR, including its Python API. Ensure you have build tools and dependencies installed. ```bash sudo apt-get update sudo apt-get install build-essential git libxml2-dev bison flex libcdk5-dev cmake python3-pip libusb-1.0-0-dev libavahi-client-dev libavahi-common-dev libaio-dev cd ~ git clone --branch v0.23 https://github.com/analogdevicesinc/libiio.git cd libiio mkdir build cd build cmake -DPYTHON_BINDINGS=ON .. make -j$(nproc) sudo make install sudo ldconfig cd ~ git clone https://github.com/analogdevicesinc/libad9361-iio.git cd libad9361-iio mkdir build cd build cmake .. make -j$(nproc) sudo make install cd ~ git clone --branch v0.0.14 https://github.com/analogdevicesinc/pyadi-iio.git cd pyadi-iio pip3 install --upgrade pip pip3 install -r requirements.txt sudo python3 setup.py install ``` -------------------------------- ### Configure PyQt GUI Layout and Widgets Source: https://pysdr.org/content/pyqt Sets up a PyQt layout by adding widgets such as sliders, labels, and a QComboBox for sample rate selection. It also connects signals from the QComboBox to update associated labels and the worker object. ```python layout.addWidget(gain_slider, 5, 0) layout.addWidget(gain_label, 5, 1) # Sample rate dropdown using QComboBox sample_rate_combobox = QComboBox() sample_rate_combobox.addItems([str(x) + ' MHz' for x in sample_rates]) sample_rate_combobox.setCurrentIndex(0) # should match the default at the top sample_rate_combobox.currentIndexChanged.connect(worker.update_sample_rate) sample_rate_label = QLabel() def update_sample_rate_label(val): sample_rate_label.setText("Sample Rate: " + str(sample_rates[val]) + " MHz") sample_rate_combobox.currentIndexChanged.connect(update_sample_rate_label) update_sample_rate_label(sample_rate_combobox.currentIndex()) # initialize the label layout.addWidget(sample_rate_combobox, 6, 0) layout.addWidget(sample_rate_label, 6, 1) central_widget = QWidget() central_widget.setLayout(layout) self.setCentralWidget(central_widget) ``` -------------------------------- ### FAM Implementation Setup in Python Source: https://pysdr.org/content/cyclostationary Sets up parameters for the FFT Accumulation Method (FAM) implementation. This involves defining the signal length (N), number of input channels (Np), offset (L), and calculating the number of windows and power of 2 for processing. ```python N = 2**14 x = samples[0:N] # Number of input channels, should be power of 2 Np = 512 L = Np//4 # Offset between points in the same column at consecutive rows in the same channelization matrix. It should be chosen to be less than or equal to Np/4 num_windows = (len(x) - Np) // L + 1 Pe = int(np.floor(int(np.log(num_windows)/np.log(2)))) P = 2**Pe N = L*P ``` -------------------------------- ### Nest Layouts in Python Source: https://pysdr.org/content/pyqt Illustrates how to nest layouts by adding one layout (e.g., QHBoxLayout) to another (e.g., QGridLayout). This allows for complex UI arrangements. ```python layout = QGridLayout() self.setLayout(layout) inner_layout = QHBoxLayout() layout.addLayout(inner_layout) ``` -------------------------------- ### Mueller and Muller Clock Recovery in Python Source: https://pysdr.org/content/sync Implements the Mueller and Muller clock recovery algorithm for timing synchronization. This technique uses a feedback loop to adjust a phase offset estimate (`mu`) to ensure samples are taken at the peak of digital symbols. It processes input samples (`samples`) and outputs synchronized samples. The loop's reactivity can be tuned by adjusting a constant (0.3 in the example). ```python mu = 0 # initial estimate of phase of sample out = np.zeros(len(samples) + 10, dtype=np.complex64) out_rail = np.zeros(len(samples) + 10, dtype=np.complex64) # stores values, each iteration we need the previous 2 values plus current value i_in = 0 # input samples index i_out = 2 # output index (let first two outputs be 0) while i_out < len(samples) and i_in+16 < len(samples): out[i_out] = samples[i_in] # grab what we think is the "best" sample out_rail[i_out] = int(np.real(out[i_out]) > 0) + 1j*int(np.imag(out[i_out]) > 0) x = (out_rail[i_out] - out_rail[i_out-2]) * np.conj(out[i_out-1]) y = (out[i_out] - out[i_out-2]) * np.conj(out_rail[i_out-1]) mm_val = np.real(y - x) mu += sps + 0.3*mm_val i_in += int(np.floor(mu)) # round down to nearest int since we are using it as an index mu = mu - np.floor(mu) # remove the integer part of mu i_out += 1 # increment output index out = out[2:i_out] # remove the first two, and anything after i_out (that was never filled out) samples = out # only include this line if you want to connect this code snippet with the Costas Loop later on ``` -------------------------------- ### Create and Add Widgets to QGridLayout in Python Source: https://pysdr.org/content/pyqt Shows how to create a grid layout and add buttons to specific rows and columns. It also illustrates how a widget can span multiple columns. ```python layout = QGridLayout() layout.addWidget(QPushButton("Button at (0, 0)"), 0, 0) layout.addWidget(QPushButton("Button at (0, 1)"), 0, 1) layout.addWidget(QPushButton("Button at (0, 2)"), 0, 2) layout.addWidget(QPushButton("Button at (1, 0)"), 1, 0) layout.addWidget(QPushButton("Button at (1, 1)"), 1, 1) layout.addWidget(QPushButton("Button at (1, 2)"), 1, 2) layout.addWidget(QPushButton("Button at (2, 0) spanning 2 columns"), 2, 0, 1, 2) self.setLayout(layout) ```