Main Content

Channel Emulation on NI USRP Radio

R2026b
Since R2026b

This example shows how to deploy a multipath channel emulation algorithm on the FPGA of an NI™ USRP™ radio. The channel emulator applies per-path fractional and integer delays and complex gains to an input signal, enabling real-time emulation of frequency-selective multipath channels for over-the-air testing.

Workflow

In this example, you follow a step-by-step guide to generate a custom FPGA image from a Simulink® model and deploy it on an NI USRP radio by using a generated MATLAB® host interface script.

For more information about how to prototype and deploy software-defined radio (SDR) algorithms on the FPGA of an NI USRP radio, see Target NI USRP Radios Workflow.

Design Overview

The channel emulator receives an input signal from a radio antenna, applies multipath channel effects, and retransmits the modified signal through a second antenna. The FPGA implements a single-input single-output (SISO) multipath channel filter that processes up to 11 propagation paths simultaneously. Each path applies a programmable fractional delay using a 16-tap polyphase FIR filter, an integer sample delay, and a complex path gain. The algorithm sums across all paths to produce the channel-filtered output. This example uses a ray tracing channel model based on a Hong Kong urban scenario to define the propagation paths with realistic delays and gains.

From the host, you program the path delays and complex gains using streaming interfaces. You can generate fading channel effects by reprogramming the channel model at run time without regenerating the bitstream.

By deploying a channel emulator on the FPGA, you can create a controlled test environment for repeatable over-the-air testing without a live propagation channel. This approach enables you to reproduce specific propagation conditions in the lab and apply identical channel profiles across test runs for consistent performance comparisons.

Requirements

To target NI USRP radio devices with Wireless Testbench™, you must install and configure third-party tools and additional support packages.

For details about which NI USRP radios you can target, see Supported Radio Devices.

Note

  • You cannot use this example with a USRP X310 radio with TwinRX daughterboards. The design in this example requires a transmit path.

  • This workflow supports generating bitstreams only on a Linux® operating system (OS). For details about host system requirements, see System Requirements.

For details about how to install and configure additional support packages and third-party tools, see Installation for Targeting NI USRP Radios.

Set Up Environment and Radio

Set up a working directory for running the example by using the openExample function in MATLAB. This function downloads the example files into a subfolder of the Examples folder in the currently running release and then opens the example. If a copy of the example exists, openExample opens the existing version of the example.

openExample("wt/ChannelEmulationOnNIUSRPRadioExample")

The working folder contains all the files you need to use this example, including helper functions and supporting files. The files you interact with are:

  • wtChannelEmulatorSL.slx — The Simulink hardware generation model. This model includes the ChannelEmulator DUT subsystem, which implements a multipath channel filter, and additional subsystems that enable you to simulate the DUT behavior.

  • ChannelEmulationOnNIUSRPRadioExample.m — The MATLAB script that you can use to simulate the behavior of the Simulink model before you generate HDL code.

  • VerifyChannelEmulationAlgorithmUsingMATLABExample.m — A live script that you can use in MATLAB to verify the algorithm running on your radio.

To program the FPGA on your radio with the bitstream that you generate in this example, and to verify the algorithm running on your radio, use the Radio Setup wizard to connect and set up your radio.

Simulink Hardware Generation Model

The Simulink model in this example implements a SISO multipath channel emulator using a hardware modeling style and uses blocks that support HDL code generation. The model applies per-path fractional and integer delays, multiplies each path by a complex gain, and sums across all paths to produce a single filtered output. It uses fixed-point arithmetic and has streaming interfaces for data, path gain, and path delay programming inputs.

Open Model

Open the Simulink model.

open_system('wtChannelEmulatorSL');

The top-level structure of the model includes the ChannelEmulator subsystem, which is the DUT. It also contains subsystems and blocks that generate input data and save output data for simulating the behavior of the DUT.

Open the ChannelEmulator subsystem.

open_system('wtChannelEmulatorSL/ChannelEmulator');

Data flows through these blocks and subsystems:

  • The DelaysStreamSlice subsystem deserializes the streaming path delay programming input. It converts serial streams into parallel integer path delays and fractional delay filter coefficients for the Channel Filter subsystem.

  • The PathGainControl subsystem buffers the streaming complex path gains. It presents them as a parallel vector synchronized with the data valid signal.

  • The Channel Filter subsystem is the core signal processing block. It applies per-path fractional and integer delays, multiplies each delayed path by its complex gain, and sums across all paths.

Open the Channel Filter subsystem.

open_system('wtChannelEmulatorSL/ChannelEmulator/Channel Filter');

The Channel Filter subsystem accepts data, path gains, integer path delays, and fractional delay coefficients. It processes the input through two stages:

  • The Path Delays subsystem applies both a fractional delay (polyphase FIR filter) and an integer sample delay to each propagation path independently.

  • The Apply Gains and Combine Paths subsystem multiplies each delayed path signal by its corresponding complex path gain and sums the contributions from all paths into a single output stream.

Open the Path Delays subsystem.

open_system('wtChannelEmulatorSL/ChannelEmulator/Channel Filter/Path Delays');

This subsystem implements per-path delay processing using a For Each Subsystem block that processes each path independently. For each path, the processing chain is:

  1. A programmable FIR filter implements the fractional sample delay. The filter uses 16 taps with coefficients derived from a polyphase decomposition with 50 interpolation phases. The coeff write block manages writing new filter coefficients when the channel is reprogrammed.

  2. A variable-length memory implements the integer sample delay. The delay value is programmable through the intDelay port.

The first path, which is the reference path, has zero fractional delay and passes through a stream synchronizer to align with the delayed paths for downstream processing.

Open the Apply Gains and Combine Paths subsystem.

open_system('wtChannelEmulatorSL/ChannelEmulator/Channel Filter/Apply Gains and Combine Paths');

This subsystem performs two operations:

  • The HDL_Complex_Multiplier block multiplies each path's delayed signal by its corresponding complex path gain. The multiplier uses a pipelined architecture suitable for HDL code generation.

  • The Sum across paths block accumulates the weighted contributions from all paths into a single output sample representing the channel-filtered signal.

Simulate Design

Verify the design by simulating the model and comparing the results against the MATLAB floating-point golden reference. Set up the simulation parameters using the helperChannelEmulatorSimulationSetup function with the ray tracing channel model. This function configures 11 static propagation paths from a Hong Kong urban scenario. It generates 4-QAM test data and designs the polyphase fractional delay filter coefficients.

sl_in = helperChannelEmulatorSimulationSetup;

Display the key simulation parameters.

fprintf('Sample Rate:            %.0f MHz\n', sl_in.SampleRate/1e6);
fprintf('Number of Paths:        %d\n', sl_in.NumPaths);
fprintf('Frame Length:           %d samples\n', sl_in.FrmLen);
fprintf('Fractional Filter Taps: %d\n', sl_in.FracFilterLen);
Sample Rate:            250 MHz
Number of Paths:        11
Frame Length:           1000 samples
Fractional Filter Taps: 16

Compute a golden reference output using the comm.ChannelFilter System object™. This System object implements the same multipath channel filtering in MATLAB. It serves as a behavioral reference for the fixed-point Simulink model.

chanFilter = comm.ChannelFilter( ...
    SampleRate=sl_in.SampleRate, ...
    PathDelays=sl_in.PathDelays, ...
    NormalizeChannelOutputs=false, ...
    FilterDelaySource='Custom', ...
    FilterDelay=sl_in.FracFilterLen/2 - 1);
yGolden = chanFilter(sl_in.Data, sl_in.CIR);

Simulate the model.

simTime = (1.5 * sl_in.FrmLen / sl_in.SampleRate) * sl_in.OversampleFactor;
out = sim('wtChannelEmulatorSL', 'StopTime', num2str(simTime));

Compare the Simulink fixed-point output against the MATLAB floating-point golden reference. The plot shows the real and imaginary components of both signals overlaid, along with the error signal highlighting quantization differences.

ySimulink = out.hdlchanfilt(:);
sigLen = min(length(ySimulink), length(yGolden));
ySimulink = ySimulink(1:sigLen);
yGolden = yGolden(1:sigLen);
sigErr = ySimulink - yGolden;

maxErrRe = max(abs(real(sigErr)));
maxErrIm = max(abs(imag(sigErr)));

figure(Position=[100 100 900 500]);
tl = tiledlayout(2, 1);
title(tl, "Channel Emulator: Simulink vs MATLAB Reference");
xlabel(tl, "Sample");
ylabel(tl, "Amplitude");

nexttile;
plot(real(yGolden), '-g');
hold on
plot(real(ySimulink), '--y');
plot(real(sigErr), '-r')
hold off
title("Real  (max error: " + num2str(maxErrRe) + ")");

nexttile;
plot(imag(yGolden), '-g');
hold on
plot(imag(ySimulink), '--y');
plot(imag(sigErr), '-r')
hold off
title("Imag  (max error: " + num2str(maxErrIm) + ")");

leg = legend(["MATLAB Reference", "Simulink HDL", "Error"]);
leg.Layout.Tile = 'east';

Configure IP Core

When you are satisfied with the simulated behavior of the model, you can proceed to integrate your design into a custom IP core by generating HDL code and mapping the model inputs and outputs to the hardware interfaces.

First, use the hdlsetuptoolpath (HDL Coder) function to set up the tool chain. Specify the path to your Vivado® bin directory. For more information, see Set Up Third-Party Tools.

hdlsetuptoolpath('ToolName','Xilinx Vivado', ...
    'ToolPath','/opt/Xilinx/Vivado/2021.1/bin');

From the Apps tab in the Simulink Toolstrip, select HDL Coder. Then open the HDL Code tab.

Configure Output Options

In the HDL Code tab, configure the output options:

  • Ensure the ChannelEmulator subsystem is pinned in the Code for option. To pin this selection, select the ChannelEmulator subsystem in the Simulink model and click the pin icon.

  • Select IP Core as the Output > IP Core option.

HDL Code tab in the Simulink Toolstrip

Configure HDL Code Generation Settings

Open the Configuration Parameters dialog box by clicking Settings in the HDL Code tab.

In the HDL Code Generation pane, ensure that Language is set to Verilog. By default, HDL Coder generates the Verilog® files in the hdlsrc folder. You can select an alternative location. If you make any changes, click Apply.

HDL Code Generation panel in the Configuration Parameters window

In the Target pane, configure these settings:

  • Under Workflow Settings, select the IP Core Generation workflow. To set Project Folder, click Browse and select a target location for saving the generated project files. If you do not specify a project folder, the software saves the generated project files in the working directory.

  • Under Tool and Device Settings, select your radio from the Target Platform list. This example uses a USRP N320 radio. If you are using a different radio, adjust the reference design parameters.

  • Under Reference Design Settings, set Reference Design to HG: 1 GigE, 10GigE.

    Set the reference design parameters to these values:

    • Number of Input Streams — Set to 3 because the DUT has three input streams: one data stream from a radio antenna, one for programming the path gains, and one for programming the path delays.

    • Number of Output Streams — Set to 1 because the DUT sends one data output stream to a radio antenna for transmission.

    • Sample Rate (S/s) — Set to a sample rate supported by your radio configuration. The channel emulation algorithm operates at the master clock rate (MCR). The verification script uses the sample rate returned by the helperChannelEmulatorSimulationSetup function.

    • Reference Design Optimization — Set to None. This setting preserves all reference design resources for maximum debugging capability during initial development.

    • DUT Clock Source — Set to Radio. When you select this setting, the DUT is clocked at the MCR used by the radio to achieve the specified sample rate.

    • Stream Port FIFO Length (Samples) — Set to Auto. This setting automatically calculates the buffer length for each DUT input and output data streaming port.

    • Register Port FIFO Length (Samples) — Set to Auto. This setting automatically calculates the buffer length for each DUT register port.

Click Apply.

HDL Code Generation > Target panel showing reference design parameters for the channel emulation example

For more information, see Configure HDL Code Generation Settings.

Map Target Interfaces

In the HDL Code tab, click Target Interface to open the Interface Mapping table in the IP Core editor. To populate the table with your user logic, click the Reload IP core settings button: Reload IP core settings and interface mapping table from model icon.

IP Core Interface Mapping table for channel emulation

The Source, Port Type, and Data Type columns are populated based on the Simulink model. In this example, the Interface column is prepopulated because the model is saved with the interface mapping already assigned.

  • The rst input register port maps to a Write Register interface.

  • The RF data input (dataIn, dataIn_valid, dataIn_last, dataIn_ready) map to a Stream Port Input0 interface. This stream carries the input signal from the receive antenna to the channel emulator.

  • The RF data output (dataOut, dataOut_valid, dataOut_last, dataOut_ready) map to a Stream Port Output0 interface. This stream carries the channel-filtered signal to the transmit antenna.

  • The pathGains_data and delays_data ports map to additional Stream Port Input interfaces. These streaming interfaces accept the channel model path gains and delays from the host.

The Interface Mapping column is populated automatically based on the signal names in the model.

To set the interface options for the data streaming interfaces, open the Set Interface Options window by clicking Options in the far right of the table.

  • For the dataIn interface options, select the first enumerated receive antenna as the source connection. For example, on a USRP N320 radio, select RF0:RX2. The DUT receives input samples from this antenna on the radio.

  • For the dataOut interface options, select the second enumerated transmit antenna as the sink connection. For example, on a USRP N320 radio, select RF1:TX/RX. The DUT sends the channel-filtered output samples to this antenna for transmission.

  • For the pathGains_data and delays_data interface options, select host as the source connection.

When you have populated the table, validate the interface mapping by clicking the Validate IP core settings button: Validate IP core settings and interface mapping icon.

For more information, see Map Target Interfaces.

Generate and Load Bitstream

To generate a bitstream from the configured IP core, select Build Bitstream > Deployment Settings.

Deployment Settings button in Build Bitstream drop-down menu

  • Ensure that the Run build process externally option is selected. This setting is the default and it ensures that the bitstream build executes in an external shell, which allows you to continue using MATLAB while building the FPGA image.

  • In the Program Target Device settings, set the IP address. The default is 192.168.10.2. If your radio has a different IP address, update this value.

Deployment Settings window

Click Build Bitstream to create a Vivado IP core project and build the bitstream. After the basic project checks complete, the Diagnostic Viewer displays a Build Bitstream Successful message along with warning messages. However, you must wait until the external shell displays a successful bitstream build before moving to the next step. Closing the external shell before this time terminates the build.

The bitstream for this project generates with the name n3xx.bit and is located in the build_N320_HG/build-N320_HG folder of the working directory after a successful bitstream build. If you are using a different radio, the name and location reflect your radio.

To load the bitstream onto the device now, select Build Bitstream > Program Target Device. Alternatively, you can load the bitstream later by using the programFPGA function in the generated host interface script.

For more information, see Generate Bitstream and Program FPGA.

Generate Host Interface Scripts

To generate MATLAB scripts that enable you to connect to and run your deployed design on your radio, in the HDL Code tab, click Host Interface Script. This step generates two scripts in your working directory based on the target interface mapping that you configured for your IP core.

  • gs_wtChannelEmulatorSL_interface.m — Host interface script that creates an fpga object for interfacing with your DUT running on the FPGA from MATLAB. The script contains code that connects to your hardware and programs the FPGA and code samples to get you started with running the algorithm on your radio. For more information, see Interface Script File.

  • gs_wtChannelEmulatorSL_setup.m — Setup function that configures the fpga object with the hardware interfaces and ports from your DUT algorithm. The function contains DUT port objects that have the port name, direction, data type, and interface mapping information, which it maps to the corresponding interfaces. For more information, see Setup Function File.

    Note

    The number of samples per host-side readPort or writePort operation, specified in the FrameSize name-value argument of the addRFNoCStreamInterface function, is set by default to 1e5. To change this value or the value of the Timeout name-value argument, edit the setup function file.

Edit Setup Function File

The generated setup function file configures the streaming interfaces with the default frame size of 1e5 samples. Before you run the verification script, update the frame sizes for the pathGains_data and delays_data streaming inputs by following these steps.

  1. Open the setup function file for editing.

    edit gs_wtChannelEmulatorSL_setup.m

  2. Identify the calls to the addRFNoCStreamInterface function for the pathGains_data and delays_data ports.

  3. Update the FrameSize value for pathGains_data to 11. This value equals the number of channel paths, because the host sends one complex gain per path.

    TX_STREAM0_FrameSize = 11;

  4. Update the FrameSize value for delays_data to 170. This value accounts for the 10 integer path delays (paths 2 through 11, because path 1 is the zero-delay reference) plus 160 fractional delay filter taps (16 taps for each of the 10 paths).

    TX_STREAM1_FrameSize = 170;

  5. Save the updated setup function file.

Verify Channel Emulation Algorithm Using MATLAB

Use this script to verify the channel emulation algorithm running on your radio. Program the FPGA with a ray tracing channel model and compare the hardware output against a MATLAB reference.

This script follows these steps:

  1. Set up and configure the radio with the generated bitstream and interfaces.

  2. Set up channel parameters for running on hardware at the device sample rate.

  3. Program the channel emulator with path delays and gains from a ray tracing scenario.

  4. Validate the impulse response of the FPGA against the comm.ChannelFilter System object.

  5. Verify the frequency-selective behavior by comparing input and output power spectra.

RF loopback configuration using an NI USRP radio and deployed channel emulator DUT. Transmit and capture paths connect through DAC and ADC blocks, with channel filter, delay control, and path gain control. MATLAB host interface streams channel model parameters.

Open Live Script

You can open this live script in MATLAB from the example working folder and use it interactively. In the Files panel, navigate to your example working folder and open VerifyChannelEmulationAlgorithmUsingMATLABExample.m.

Select Radio

Call the radioConfigurations function. The function returns all available radio setup configurations that you saved using the radioSetupWizard wizard.

savedRadioConfigurations = radioConfigurations;

To update the menu with your saved radio configuration names, click Update. Then select the radio to use with this example.

savedRadioConfigurationNames = string({savedRadioConfigurations.Name});
radioConfig = savedRadioConfigurationNames(1) ;

Evaluate the transmit and receive antennas available on your radio device. You select available antennas to transmit and capture test data later in the script.

availableReceiveAntennas = hCaptureAntennas(radioConfig);
availableTransmitAntennas = hTransmitAntennas(radioConfig);

Create Radio Object

Use the radioConfigurations function to create a radio object.

radio = radioConfigurations(radioConfig);

Create a usrp System object with the specified radio. This System object controls the radio hardware.

device = usrp(radio);

Program FPGA

If you have not yet programmed your device with the bitstream, select the program bitstream option. Update the code with your bitstream and hand-off information files. You can find these in the generated host interface script, gs_wtChannelEmulatorSL_interface.

programBitstream = false;
if (programBitstream)
     programFPGA(device, ...
         "build_N320_HG/build-N320_HG/n3xx.bit", ...    % replace with your .bit file
         "build_N320_HG/build/usrp_n320_fpga_HG.dts");  % replace with your .dts file
end

Configure the DUT interfaces according to the hand-off information file using the describeFPGA function. The function additionally sets the SampleRate, DUTInputAntennas, and DUTOutputAntennas properties on the usrp System object based on the selections you made in Simulink.

% replace with your .mat handoff file
describeFPGA(device, "wtChannelEmulatorSL_wthandoffinfo.mat");

Set Up Channel Parameters

Configure the ray tracing channel model using the helperChannelEmulatorSimulationSetup function. Pass the device sample rate so that path delays and fractional filter coefficients are computed at the correct rate for your radio. The function returns parameters for 11 static propagation paths derived from a Hong Kong urban scenario.

sl_in = helperChannelEmulatorSimulationSetup(device.SampleRate);

Site Viewer showing a 3D urban environment of Hong Kong with dense buildings and waterfront. Two wireless nodes connect through a multipath channel, with links highlighted in yellow and a power scale in dBm.

Display the key channel parameters.

fprintf('Sample Rate:            %.0f MHz\n', sl_in.SampleRate/1e6);
Sample Rate:            250 MHz
fprintf('Number of Paths:        %d\n', sl_in.NumPaths);
Number of Paths:        11
fprintf('Fractional Filter Taps: %d\n', sl_in.FracFilterLen);
Fractional Filter Taps: 16

Configure Device

Set the LoopbackMode property to FPGA to loop back each transmit antenna to an associated receive antenna on the FPGA. To use FPGA loopback, your DUT must be configured with an input and output antenna connection that is a valid loopback antenna pair. For details, see the table in the LoopbackMode property description. Alternatively, select Disabled to transmit and receive data over the air.

device.LoopbackMode = "FPGA";

By default, the DUT streaming input and output are connected to the antennas you selected in the Simulink workflow. To use different antennas, uncomment this code and specify a value for the DUTInputAntennas and DUTOutputAntennas properties.

% device.DUTInputAntennas = "RF0:RX2";
% device.DUTOutputAntennas = "RF1:TX/RX";

Specify a transmit antenna and a capture antenna to exercise the channel emulator DUT from MATLAB.

device.TransmitAntennas = availableTransmitAntennas(1);
device.CaptureAntennas = availableReceiveAntennas(2);

If you are transmitting and receiving over the air using antennas, specify the center frequency and radio gains for each antenna. If FPGA loopback is enabled, these properties have no effect.

Specify the center frequency for the transmit and receive antennas. For each antenna pair, ensure transmit and receive center frequencies are equal. To isolate the two pairs of antennas, either use loopback cables or separate the antenna pairs in frequency. If you have a USRP N310 radio, specifying multiple center frequencies is only possible using independent channels. For details, see the ReceiveCenterFrequency and TransmitCenterFrequency property descriptions.

Fc = 2.4e9;
mlTransmitCenterFrequency = Fc;
dutInputCenterFrequency = Fc;
dutOutputCenterFrequency = Fc+200e6;
mlCaptureCenterFrequency = Fc+200e6;
device.ReceiveCenterFrequency = [dutInputCenterFrequency, mlCaptureCenterFrequency];
device.TransmitCenterFrequency = [dutOutputCenterFrequency, mlTransmitCenterFrequency];

Specify the radio gains for the specified antennas. Set the value according to your RF setup and hardware.

dutInputRadioGain = 30;
dutOutputRadioGain = 30;
mlCaptureRadioGain = 30;
mlTransmitRadioGain = 30;
device.ReceiveRadioGain = [dutInputRadioGain, mlCaptureRadioGain];
device.TransmitRadioGain = [dutOutputRadioGain, mlTransmitRadioGain];

Create and Set Up fpga Object

Create an fpga object to interface with the DUT ports you designed in Simulink.

dut = fpga(device);

Set up the fpga object using the generated setup function. If you have not updated this file to set the frame size for the path gain and delay streaming inputs, first follow the steps in Edit Setup Function File.

gs_wtChannelEmulatorSL_setup(dut);

Set Up usrp Object

Establish a connection with the radio hardware by calling setup on the usrp System object.

setup(device);

Program Channel Emulator

Reset the channel emulator by pulsing the reset register.

writePort(dut, "rst", ones([1, 1]));
writePort(dut, "rst", zeros([1, 1]));

Write the path gains to the FPGA. The pathGains_data streaming port accepts a vector of complex path gains for all paths at a single time instant.

writePort(dut, "pathGains_data", sl_in.PathGains(:,1));

Construct and write the delays vector to the delays_data streaming port. The vector contains the integer path delays for paths 2 through NumPaths. Path 1 is the reference path with zero delay. The vector also includes the serialized fractional delay FIR filter coefficients reinterpreted as unsigned 16-bit integers.

delays = [sl_in.PathIntegerDelays(2:end)'; ...
          reinterpretcast(sl_in.FracDelayCoeffs(:), numerictype(0,16,0))];
writePort(dut, "delays_data", delays);

Verify Impulse Response

Validate the FPGA channel filter by transmitting an impulse and comparing the hardware output against the expected response from the comm.ChannelFilter System object.

Create an impulse signal. Place the delta function at an offset that accounts for the maximum integer path delay plus a margin.

impulseOffset = max(double(sl_in.PathIntegerDelays)) + 50;
impulseLen = sl_in.FrmLen;
impulse = zeros(impulseLen, 1);
impulse(impulseOffset + 1) = complex(fi(1 - 2^-15, 1, 16, 15), fi(0, 1, 16, 15));

Transmit the impulse continuously and capture the DUT output. Allow the transmit signal to propagate through the DUT before capturing. Convert the captured data to the channel emulator output data format of 16-bit signed integer with 13 fractional bits.

txData = int16(impulse * 2^15);
captureLen = impulseLen * 5;

transmit(device, txData, "continuous");
device(captureLen);
[dataOut, numSamps] = capture(device, captureLen);
dataOut = double(reinterpretcast(fi(dataOut, 1, 16, 0), numerictype(1, 16, 13)));

Compute the expected impulse response using the comm.ChannelFilter System object with the same channel parameters.

chanFilter = comm.ChannelFilter( ...
    SampleRate=sl_in.SampleRate, ...
    PathDelays=sl_in.PathDelays, ...
    NormalizeChannelOutputs=false, ...
    FilterDelaySource="Custom", ...
    FilterDelay=sl_in.FracFilterLen/2 - 1);
yGolden = chanFilter(impulse, sl_in.CIR(1,:));

Align the hardware output with the expected response using cross-correlation.

[xc, lags] = xcorr(dataOut, yGolden);
[~, maxIdx] = max(abs(xc));
offset = lags(maxIdx);

startIdx = max(1, offset + 1);
endIdx = min(numSamps, startIdx + length(yGolden) - 1);
alignedHw = dataOut(startIdx:endIdx);
alignedRef = yGolden(1:length(alignedHw));

Plot the FPGA impulse response against the MATLAB reference.

nShow = 200;
t_us = (0:nShow-1) / sl_in.SampleRate * 1e6;

figure;
stem(t_us, real(alignedRef(impulseOffset:impulseOffset+nShow-1)), 'b', 'MarkerSize', 3);
hold on
stem(t_us, real(alignedHw(impulseOffset:impulseOffset+nShow-1)), 'r', 'MarkerSize', 3);
xlabel('Time (\mus)'); ylabel('Amplitude');
title('Channel Impulse Response: MATLAB vs FPGA');
legend('MATLAB Reference', 'FPGA Output', 'Location', 'northeast');
xlim([0 t_us(end)]);

Figure contains an axes object. The axes object with title Channel Impulse Response: MATLAB vs FPGA, xlabel Time ( mu s), ylabel Amplitude contains 2 objects of type stem. These objects represent MATLAB Reference, FPGA Output.

Verify Channel Frequency Response

Transmit a wideband signal through the channel emulator and compare the input and output power spectra. Verify that frequency-selective fading from the 11-path channel is visible as spectral shaping on the output.

Generate a wideband test signal using random QPSK symbols with square-root raised cosine pulse shaping.

FrmLen = 50000;
sps = 4;
rolloff = 0.25;

numSyms = ceil(FrmLen / sps) + 10;
txSyms = qammod(randi([0 3], numSyms, 1), 4, UnitAveragePower=true);
txFilter = comm.RaisedCosineTransmitFilter( ...
    RolloffFactor=rolloff, ...
    FilterSpanInSymbols=8, ...
    OutputSamplesPerSymbol=sps);
txFiltered = txFilter(txSyms);
txShaped = txFiltered(1:FrmLen);
txShaped = txShaped / max(abs(txShaped)) * (1 - 2^-15);
txWideband = int16(txShaped * 2^15);

Transmit the wideband signal and capture the DUT output. Allow the transmit signal to propagate through the DUT before capturing. Convert the captured data to the channel emulator output data format of 16-bit signed integer with 13 fractional bits.

transmit(device, txWideband, "continuous");
device(FrmLen);
[rxData, nSamps] = capture(device, FrmLen);
rxData = double(reinterpretcast(fi(rxData, 1, 16, 0), numerictype(1, 16, 13))); 

Compute and plot the power spectral density of the transmitted and received signals to view the effect of the channel.

sa = spectrumAnalyzer( ...
    SampleRate=sl_in.SampleRate, ...
    NumInputPorts=2, ...
    SpectrumType="Power density", ...
    Method="Welch", ...
    ChannelNames={'Transmitted', 'Received (after channel)'}, ...
    Title="Channel Emulator: Input vs Output Power Spectrum", ...
    ShowLegend=true);
nAnalyze = min(FrmLen, nSamps);
sa(txShaped(1:nAnalyze), rxData(1:nAnalyze));
release(sa);

Release Hardware Resources

Release the hardware resources.

release(dut);
release(device);

Next Steps

Because the path gains and delays are programmed at run time, you can adapt the channel emulator for other channel modeling scenarios without modifying the design or regenerating the bitstream. For example, you can:

  • Emulate time-varying fading conditions by updating the path gains periodically during transmission. Use the host interface script to reprogram the path gains stream while the channel emulator is running.

  • Customize the ray tracing scenario by changing the propagation environment, transmitter and receiver positions, or materials to generate path delays and gains that match your test conditions.

  • Emulate different channel models by writing path gains and delays from other MATLAB channel objects to the FPGA. For example, use the 5G Toolbox™ nrCDLChannel (5G Toolbox) or nrTDLChannel (5G Toolbox) fading channel models.

See Also

Objects

Topics