GPU Digital Signal Processing¶
A major feature of Deepwave's AIR-T is the incorporation of the embedded GPU within the digital signal processing (DSP) chain. For users who wish to perform signal processing with the GPU, Deepwave recommends using the CuPy library.
CuPy is a Python library that enables GPU-accelerated computing with a NumPy-compatible API, making it ideal for high-performance signal processing on the AIR-T. By leveraging the AIR-T's onboard NVIDIA GPU, CuPy allows for fast numerical computations, such as matrix operations and FFTs, critical for real-time RF and SDR applications. This acceleration enhances the AIR-T’s capability to handle computationally intensive tasks efficiently, supporting custom development and advanced workflows. CuPy acts as a drop-in replacement to run existing NumPy/SciPy code on NVIDIA CUDA platforms.
References:
List of NumPy / SciPy APIs and its corresponding CuPy implementations
Tutorial¶
The following tutorial illustrates how to use CuPy to execute a polyphase resampling filter on the AIR-T and measure the throughput data rate.
This tutorial applies to AirStack 2.0+. If you are using a AirStack <2.0, see the archived tutorial here.
Create CuPy Environment¶
CuPy can be installed on your system using pip. For this tutorial you will also need to install numba using pip and a compatible version of numpy. Ideally this setup would be contained in a conda environment.
apt-get install python3-pip
python -m pip3 install -U setuptools pip3
pip install "numpy>=1.22,<2.0"
pip install cupy-cuda12x
pip install numba
Example: Polyphase Filter¶
The following Python code provides an example of how to use the AIR-T SoapySDR interface in conjunction with the resample_poly
function of CuPy. This is the updated source code from the Deep Learning and Signal Processing Webinar to use cuSignal functions now integrated with CuPy.
The following source code shows how to execute the polyphase resampling filter on the AIR-T and measure the throughput datarate.
#!/usr/bin/env python3
import SoapySDR
from matplotlib import pyplot as plt
import cupy as cp
import cupyx.scipy.signal as signal
from numba import cuda
buffer_size = 2 ** 19 # Number of complex samples per transfer
t_test = 20 # Test time in seconds
freq = 1350e6 # Tune frequency in Hz
fs = 62.5e6 # Sample rate
# Create polyphase filter
fc = 1. / max(16, 25) # cutoff of FIR filter (rel. to Nyquist)
nc = 10 * max(16, 25) # reasonable cutoff for our sinc-like function
win = signal.firwin(2 * nc + 1, fc, window=('kaiser', 0.5))
win = cp.asarray(win, dtype=cp.float32)
# Init buffer and polyphase filter
buff = cuda.mapped_array(buffer_size, dtype=cp.complex64)
s = signal.resample_poly(buff, 16, 25, window=win)
# Initialize the AIR-T receiver using SoapyAIRT
sdr = SoapySDR.Device(dict(driver="SoapyAIRT")) # Create AIR-T instance
sdr.setSampleRate(SoapySDR.SOAPY_SDR_RX, 1, fs) # Set sample rate
sdr.setGainMode(SoapySDR.SOAPY_SDR_RX, 1, True) # Set the gain mode
sdr.setFrequency(SoapySDR.SOAPY_SDR_RX, 1, freq) # Tune the frequency
rx_stream = sdr.setupStream(SoapySDR.SOAPY_SDR_RX, SoapySDR.SOAPY_SDR_CF32, [1])
sdr.activateStream(rx_stream)
# Run test
n_reads = int(t_test * fs / buffer_size) + 1
for i in range(n_reads):
sr = sdr.readStream(rx_stream, [buff], buffer_size)
s = signal.resample_poly(buff, 16, 25, window=win)
sdr.deactivateStream(rx_stream)
sdr.closeStream(rx_stream)
gbps = n_reads * len(buff) * buff.itemsize * 8 / (2 ** 30) / t_test
print('Data Rate = {:1.3f} Gbps on GPU'.format(gbps))
# Plot Power Spectral Density
plt.figure(figsize=(7, 5))
plt.subplot(211)
plt.psd(cp.asnumpy(buff), Fs=fs, Fc=freq, NFFT=16384)
plt.ylim((-160, -75))
plt.subplot(212)
plt.psd(cp.asnumpy(s), Fs=fs * 16 / 25, Fc=freq, NFFT=16384)
plt.ylim((-160, -75))
plt.show()