One of the biggest advantage of the Software Defined Radio techniques is the ability to reconfigure the different parameters at run-time. Typically one would like to change the modulation and the bandwidth depending on the context or the user input.
A typical Digital Down Converter architecture can be depicted as follows:
While very close to the analog world (except we consider here complex signals), this block requires a lot of floating-point instructions because of the two filters after the mixing stage. For a proper rejection of the unwanted signals, a large number of taps is required.
When more than one single DDC is required in the application, the CPU is then spending most of its time in the filtering. Many techniques have been developed to optimize this step, for example by combining multiple stages of filtering+decimation. These tricks do give interesting results but are not optimum when multiple outputs (with different bandwidths) are needed.
We have designed a GPU based multichannel DDC that solves this issues and offloads the heavy processing to the parallel processor. Internally it works by using and reusing FFT (the Overlap Save method) plus some tricks to handle the DDS properly.
A typical use case is described in the picture below where two tasks are receiving two different bands from the same radio simultaneously.
The number of outputs depends on the number of processors of the GPU, but is generally between 8 (small GPU like Jetson Nano) and more than 256 ( GTX series on Core i9 PC).
Now lets have a short look at the API we propose in the Virtual Machine. We start by opening a device, a simple RTL-SDR receiver in this example. Note that before trying to change the sample rate we check and wait that no other task is currently streaming reception from the device.
We now allocate a DDCBank object and configure it for 8 channels max and then create a first channel of 50kHz. This channel is tuned (local oscillator) to -100 kHz. As the center of the RTLSDR receiver was tuned to 466.1 MHz, this channel will be receiving signals around 466 MHz.
Each channel as a unique identifier (UUID). Any task knowing this ID can retrieve data from the DDC Channel. We now start the channel (that will start the receiver and put it in streaming mode if that was not already the case) and we will pull IQ blocks.
The following example is a full multi-task example. The DDCBank.js script creates a pool of 8 DDC and starts 8 independant tasks to process the signals.
At line 27, a task is created with the channel UUID passed as parameter. The running task does two things with each IQ block it receives from the DDC :
- It writes the IQ samples to a flat IQ File (line 17 and 18)
- It counts the number of samples and stops when 100 000 samples have been retrieved.
Additional notes :
- The channel UUID is retrieved by using the
argv()functions [lines 1 to 5];
- A reference to the DDCBankChannel object is retrieved and checked [lines 9 to 15];
- The file is created and connected to a queue lines 17 and 18;
Here is the task script :