OFDM Receiver Using Software Defined Radio

This example shows how to design an orthogonal frequency division multiplexing (OFDM) receiver for a single-input single-output (SISO) channel using a software-defined radio (SDR). The OFDM receiver captures and demodulates the OFDM signal that the OFDM Transmitter Using Software-Defined Radio example sends. The OFDM receiver design includes sample bufferring for timing adjustment, filtering, carrier frequency adjustment, and OFDM demodulation.

Required Hardware and Software

To run this example, you need one of these SDRs and the corresponding software support package.

The example requires two MATLAB™ sessions, one for the transmitter and one for the receiver. You run the OFDM Transmitter Using Software-Defined Radio example in one MATLAB session to transmit the OFDM signal.

Set OFDM Frame Parameters

Set the OFDM parameters, including the FFT length, cyclic prefix length, and number of subcarriers. The pilot subcarrier spacing and channel bandwidth are fixed at 30 KHz and 3 MHz, respectively.

After configuring the OFDM parameters, set the data parameters. Set the modulation order, code rate, number of symbols per frame, and the number of frames per transmission.

You can enable or disable the scopes for visualization, however for long simulations, it is recommended to disable the scope. To control the display of the diagnostic output text, enable or disable the verbosity as needed. To view the decoded data in each frame, enable the print data flag

% The chosen set of OFDM parameters: OFDMParams.FFTLength = 128; % FFT length OFDMParams.CPLength = 32; % Cyclic prefix length OFDMParams.NumSubcarriers = 72; % Number of sub-carriers in the band OFDMParams.Subcarrierspacing = 30e3; % Sub-carrier spacing of 30 KHz OFDMParams.PilotSubcarrierSpacing = 9; % Pilot sub-carrier spacing OFDMParams.channelBW = 3e6; % Bandwidth of the channel 3 MHz % Data Parameters dataParams.modOrder = 4; % Data modulation order dataParams.coderate = "1/2"; % Code rate dataParams.numSymPerFrame = 30; % Number of data symbols per frame dataParams.numFrames = 25; % Number of frames to transmit dataParams.enableScopes = true; % Switch to enable or disable the visibility of scopes dataParams.verbosity = false; % Control to print the output diagnostics at each level of receiver processing dataParams.printData = false; % Control to print the output decoded data

Initialize Receiver Parameters

The helperGetRadioParams function initializes the receiver System object™ ofdmRx. Assign the name of the radio you are using for receiving the OFDM signal to the variable radioDevice . Set the receiver gain and operating center frequency.

The helperGetRadioRxObj function initializes the radio receiver System object.

radioDevice = "B210"; % Choose radio device for reception centerFrequency = 3e9; % Center Frequency gain = 60; % Set radio gain

The helperOFDMSetParamsSDR function initializes transmit-specific and common transmitter and receiver parameters required for the OFDM simulation and the helperGetRadioParams function initializes the paramters required for the receiver System object™ radio.

[sysParam,txParam,transportBlk] = helperOFDMSetParamsSDR(OFDMParams,dataParams); sampleRate = sysParam.scs*sysParam.FFTLen; % Sample rate of signal ofdmRx = helperGetRadioParams(sysParam,radioDevice,sampleRate,centerFrequency,gain);
Checking radio connections.
[radio,spectrumAnalyze,constDiag] = helperGetRadioRxObj(ofdmRx);

Execute Receiver Loop

Synchronization

The OFDM receiver checks the sample buffer for the synchronization symbol to find the starting point of the data frames within the received OFDM signal. The receiver correlated the received OFDM signal with the known synchronization symbol. Once the OFDM receiver detects a high correlation peak, it identifies the position where it finds the synchronization symbol as the start of the frame

Frequency Offset Estimation and Correction

The OFDM receiver estimates and corrects the frequency and timing offset introduced to the transmitted OFDM signal due to channel impairments. The OFDM receiver checks the receiver buffer for the required number of frames and performs automatic frequency correction over each symbol. The receiver averages the frequency correction across the subcarriers and then across every six symbols, considering a group of six symbols as a slot. The receiver considers the overall average value of these corrections as the frequency offset and compensates this frequency offset across the entire frame.

The receiver is considered camped to a based station upon achieving successful synchronization and channel impairment correction.

Receiver Processing

This process is the reverse of the process that happens at the transmitter.

Channel Estimation and Equalization

Initially, the OFDM receiver performs channel estimation on the OFDM demodulated reference symbols. To remove the effects of time-varying fading, the receiver selects two reference symbols from adjacent frames to estimate the channel at two different points in time. The receiver then linearly interpolates the channel estimates between the two reference symbols to get the channel estimates for the header and data symbols. The ofdmEqualize function then equalizes the reference symbols and data symbols using the channel estimates.

Header Decoding

The receiver extracts and decodes the header symbols to get the data symbol parameters such as FFT length, subcarrier modulation scheme, and code rate. The receiver uses these parameters to demodulate and decode the data symbols.

Data Decoding

The common phase error (CPE) affects all subcarriers equally and the OFDM receiver uses the pilot symbols within the data symbols to estimate CPE. The helperOFDMRx function corrects the phase errors in the data symbols and the qamdemod function soft decodes the data subcarriers into log likelihood ratios (LLRs).

The receiver then deinterleaves the demodulated bitstream and the vitdec function performs maximum likelihood decoding using the Viterbi algorithm. The descrambler descrambles the decoded bits, and the comm.CRCDetector computes the cyclic redundancy check (CRC) and compares it with the appended CRC.

OFDMRxMaster.png

% Clear all the function data as they contain some persistent variables clear helperOFDMRx helperOFDMRxFrontEnd helperOFDMRxSearch helperOFDMFrequencyOffset; close all; errorRate = comm.ErrorRate(); toverflow = 0; % Receiver overflow count rxObj = helperOFDMRxInit(sysParam); BER = zeros(1,dataParams.numFrames); for frameNum = 1:dataParams.numFrames sysParam.frameNum = frameNum; [rxWaveform, ~, overflow] = radio(); toverflow = toverflow + overflow; % Run the receiver front-end only when there is no overflow if ~overflow rxIn = helperOFDMRxFrontEnd(rxWaveform,sysParam,rxObj); % Run the receiver processing [rxDataBits,isConnected,toff,rxDiagnostics] = helperOFDMRx(rxIn,sysParam,rxObj); sysParam.timingAdvance = toff; % Collect bit and frame error statistics if isConnected % Continuously update the bit error rate using the |comm.ErrorRate| % System object berVals = errorRate(. transportBlk((1:sysParam.trBlkSize)).', . rxDataBits); BER(frameNum) = berVals(1); if dataParams.printData % As each character in the data is encoded by 7 bits, decode the received data up to last multiples of 7 numBitsToDecode = length(rxDataBits) - mod(length(rxDataBits),7); recData = char(bit2int(reshape(rxDataBits(1:numBitsToDecode),7,[]),7)); fprintf('Received data in frame %d: %s',frameNum,recData); end end if isConnected && dataParams.enableScopes constDiag(complex(rxDiagnostics.rxConstellationHeader(:)), . complex(rxDiagnostics.rxConstellationData(:))); end if dataParams.enableScopes spectrumAnalyze(rxWaveform); end end end
Sync symbol found. Estimating carrier frequency offset . Receiver camped. .