Modeling a Communications System Using the Communications Toolbox

In this example, we use the Communications Toolbox to build a simple communications system. We'll start by formulating a random sequence of bits, encode those bits using a convolutional code, and then use a Gray-coded 16-QAM modulation scheme before pulse shaping using a raised cosine filter. Then we'll introduce noise using an AWGN channel. Next, we recover the signal and calculate the bit error rate.

At the end of this example, we provide a full list of links to the functions we use during this example.

Contents

Generating a Random Binary Message

Our first task in this communication system is to generate a random binary message to transmit. We do this by using the randint function in the Communications Toolbox. The bits will be modulated using 16-QAM, so each symbol will be 4 bits long. We use the following code to create the sequence and display the first 60 symbols:

M = 16;                     % Size of signal constellation
k = log2(M);                % Number of bits per symbol
n = 1e4;                    % Number of total symbols to simulate

% Create a binary signal as a column vector
x = randint(n*k,1);         % Random binary signal

% Display the first 60 random bits
stem(x(1:60),'filled');
title('Random Bits');
xlabel('Number of Bits');
ylabel('Binary Value');
set(gca,'yTick',[0 0.5 1],'XLim',[-2 62],'YLim',[-0.2 1.2],'Box','on');
set(gcf,'Color','w')

Figure 1: The first 60 random bits.

Encoding Bits with a Convolutional Code

Now that we have created the random bits, our next step is to encode them. The Communications Toolbox includes a variety of coding schemes, including BCH codes, Reed-Solomon codes, cyclic codes, and convolutional codes. This example uses an industry-standard rate 1/2 convolutional coder. This diagram illustrates the matrix representation of our code:

Figure 2: The polynomial representation of our convolutional code.

The convenc function in the Communications Toolbox uses the trellis representation of a convolutional code to encode a sequence. We use the poly2trellis function to convert from the polynomial representation of a code to the trellis representation. In the next code example, we use these commands to encode the bit sequence convolutionally. Note that the codegen function is in octal format. See the documentation on the poly2trellis function for more information.

% Declare variables
codeRate = 1/2;                             % Code Rate
constlen = 7;                               % Constraint Length
codegen = [171 133];                        % Generator Polynomials
trellis = poly2trellis(constlen, codegen);  % Create Trellis structure

% Encode transmitted sequence
xEnc = convenc(x, trellis);

Generating a Bit-to-Symbol Mapping Using a Gray Code

After channel coding, we use a Gray code as the source code. Recall that one property of a Gray code is that any two adjacent signals in a constellation have a Hamming distance of 1. The first step in Gray coding is generating a map that satisfies the above condition. The scatterplot function in the Communications Toolbox shows the resulting constellation:

% Generate Gray code mapping
% Start from a PSK constellation with sqrt(M) points
grayPsk = bitxor(0:sqrt(M)-1,floor((0:sqrt(M)-1)/2))';
% Use it to create a QAM constellation with M points
mapping = repmat(grayPsk,1,sqrt(M))+repmat(sqrt(M)*grayPsk',sqrt(M),1);
mapping = mapping(:);

% Display Bit-to-Symbol mapping
t = qammod(mapping,M);
scatterplot(t);
set(get(gca,'Children'),'Marker','d','MarkerFaceColor','auto');
hold on;

% Add labels showing the binary sequence at each constellation point
for jj=1:length(t)
    text(real(t(jj))-0.5,imag(t(jj))+0.5,dec2base(jj-1,2,4));
end
set(gca,'yTick',(-(k+1):2:k+1),'xTick',(-(k+1):2:k+1),...
    'XLim',[-(k+1) k+1],'YLim',[-(k+1) k+1],'Box','on',...
    'YGrid','on', 'XGrid','on');
xlabel ('In-Phase');
hold off;
set(gcf,'Color','w')

Figure 3: A scatter plot of our Gray-coded QAM modulation.

We've generated the Gray code mapping. Next, we separate the sequence of bits into 4-bit symbols, convert each one to decimal format, and map it the appropriate Gray-coded sequence:

% Convert the sequence from a column vector to a 4-column matrix
xSym = reshape(xEnc,k,n/codeRate).';

% Convert each row of the matrix from binary to decimal
xSym = bi2de(xSym, 'left-msb');

% Permute the transmitted sequence using Gray code mapping
xSym = mapping(xSym+1);

% Display the first 35 transmitted symbols
figure; stem(xSym(1:35));
title('Random Symbols');
xlabel('Number of Symbols');
ylabel('Integer Value {0..15}');
set(gca,'yTick',(0:M-1),'XLim',[-2 35+2],'YLim',[-0.2 M-1+.2],...
    'Box','on','YGrid','on');
set(gcf,'Color','w')

Figure 4: The first 35 random symbols.

Modulating Using 16-QAM

Now we have convolutionally coded the sequence of bits and mapped them to the decimal numbers 1-16 using a Gray code. Next, we use the qammod function to convert the decimal numbers 1-16 to points in the two-dimensional space that is used to transmit the code. We'll also display an eyediagram with the eyediagram function in the Communications Toolbox.

% Modulate the signal
xMod = qammod(xSym,M);

% Display eyediagram plot of modulated symbols
eyediagram(xMod(1:100),2);
subplot (2,1,2)
xlabel ('Time');
set (gcf, 'Color', 'w')

Figure 5: Eyediagram of our QAM modulation.

Pulse Shaping Signals Using an FIR RRC

We have modulated the sequence, and now we design an FIR root-raised cosine filter to reduce intersymbol interference (ISI). This is a common filter in communications systems. We'll follow a two step process: first, we'll create the filter, and then we'll use it to filter the signal. Let's use the Communications Toolbox function rcosine to create the filter.

% Define filter variables
filtOrder = 40; overSamp = 4;
delay = filtOrder/(overSamp*2);
rollOff = .25;

% Create filter
rrcFilter = rcosine(1,overSamp,'fir/sqrt',rollOff,delay);

Next, let's use the Signal Processing Toolbox function fvtool to show the filter's impulse response. We can also use fvtool to show the magnitude response, phase response, group delay, and other characteristics of a filter.

% Display impulse response of Transmit filter
hFV = fvtool(rrcFilter,1,'Analysis','Impulse');
xlabel ('Samples');
set (gcf, 'Color', 'w')

Figure 6: Impulse response of RRC filter.

After creating the root-raised cosine filter, our next step is to filter the incoming data stream. To further reduce the risk of ISI, we upsample the sequence using the Signal Processing Toolbox function upsample. Then we filter it with the RRC using the MATLAB filter function. The resulting plots show the first 120 bits of the sequence before being filtered and after being filtered. The real and imaginary parts are plotted separately.

% Upsample signal
yModUp = upsample(xMod,overSamp);
% Pad signal with zeros to flush filter
yModUp = [yModUp; zeros(filtOrder,1)];
% Filter upsampled signal
yTx = filter(rrcFilter,1,yModUp);

% Plot the signal before and after filter
figure;
subplot(2,1,1); % Real part
stem(real(yModUp(1:120))); hold on;
plot(real(yTx(1+delay*overSamp:80+delay*overSamp)),'r-');
xlabel('Samples'); ylabel('Amplitude');
title ('Transmitted signal after Tx Filter - Real');
legend ('Digital signal before RRC', 'Analog signal after RRC')
subplot(2,1,2); % Imaginary part
stem(imag(yModUp(1:120))); hold on;
plot(imag(yTx(1+delay*overSamp:80+delay*overSamp)),'r-');
xlabel('Samples');
ylabel('Amplitude');
title ('Transmitted signal after Tx Filter - Imag');
legend ('Digital signal before RRC', 'Analog signal after RRC')
hold off;
set (gcf, 'Color', 'w')

Figure 7: Comparison of signal before and after RRC.

Adding AWG Noise to the Transmitted Signal

We have encoded and filtered the signal, and it is ready to be transmitted. The Communications Toolbox includes several kinds of channels, including Rician channels, Rayleigh channels, and AWGN channels. In this example, we use an AWGN channel to add white Gaussian noise to the signal. We'll also use the scatterplot function to plot the received and transmitted signals for comparison:

% SNR per coded bit
EbNo = 10;

% SNR per uncoded bit
EsNo = EbNo+10*log10(k)-10*log10(overSamp)-10*log10(1/codeRate);

% Add the noise
yNoisy = awgn(yTx,EsNo,'measured');

% Plot received signal vs. transmitted signal
h=scatterplot(yNoisy(1:overSamp:end).*sqrt(overSamp),1,0,'b.');
hold on;
scatterplot(xMod,1,0,'rx',h);
set(get(get(h,'children'),'children'),'MarkerSize',10,'LineWidth',4);
title('Received vs. Transmitted Signal (Downsampled for visualization)');
axis([-(k+1) k+1 -(k+1) k+1]);
xlabel ('In-Phase');
hold off;
set (gcf, 'Color', 'w')

Figure 8: Symbols before and after noise.

Filtering Received Signals Using the RRC Filter

The signal has now been transmitted over the channel and it needs to be recovered. The steps to recover the original signal are as follows:

  1. Recover the signal from the RRC.
  2. Demodulate the signal.
  3. Undo the Gray code.
  4. Decode the convolutional code.

Step 1: Recovering the Signal

The first step is to recover the signal from the RRC. One of the advantages of an RRC is that we can use the same filter to recover the signal that we used to encode it. After decoding, we downsample the signal, because it was upsampled before it was filtered. After the data is filtered and downsampled, we plot the transmitted signal and the received signals:

% Filter received signal using Tx filter
yRx = filter(rrcFilter,1,yNoisy);
% Downsample received signal
yRxDown = downsample(yRx,overSamp);
% Compensate for filter delay
yRxDown = yRxDown(filtOrder/overSamp+1:end);

% Plot the received signal, the received analog signal after the RRC, and
% the resulting digital signal
figure;
subplot(2,1,1);
plot(real(yNoisy(delay*overSamp+1:delay*overSamp+80)),'ko-');
hold on;
plot(real(yRx(2*delay*overSamp+1:2*delay*overSamp+80)),'bx-');
stem(upsample(real(yRxDown(1:80/overSamp)),overSamp),'r-');
grid on;
axis([0 80 -4 4]);
legend('Received signal','Received filtered signal','Rx sampling point');

% For comparison, replot the transmitted signal
subplot(2,1,2);
plot(real(yTx(1+delay*overSamp:80+delay*overSamp)),'rd-.');
hold on;
stem(real(yModUp(1:80)),'b');
xlabel('Samples');
ylabel('Amplitude');
legend('Transmitted filtered signal','Original Tx signal');
axis([0 80 -4 4]);
grid on;
set(gcf,'Color','w')

Figure 9: Comparison of signal before and after RRC.

Step 2: Demodulating the Received Signal

After filtering the signal with the RRC, we'll demodulate the signal using the qamdemod function in the Communications Toolbox.

ySym = qamdemod(yRxDown,M);

Step 3: Recovering Bits from Symbols Using Gray Coding

Next, let's demap the received data using the Gray mapping. Then we can compare the sequence to the original sequence after it was encoded with the convolutional encoder. We use the biterr function to do this comparison:

[dummy demapping] = sort(mapping);
demapping = demapping - 1;
ySym = demapping(ySym+1);

% Convert integers to bits
yBits = de2bi(ySym,'left-msb');
yBits = reshape(yBits.',numel(yBits),1);

Step 4: Decoding the Convolutional Code

The last step in the system is to decode the code using a Viterbi decoder. To do this, we use the vitdec function in the Communications Toolbox. The vitdec function uses the same trellis for encoding and decoding the sequence. This function can specify both the initial state of a trellis and its final state. It can also decode using hard-decision or soft-decision decoding. Our example uses hard-decision decoding:

% Define variables
tblen = 32;     % Traceback length

% Decode received signal assuming an all-zero initial state
y = vitdec(yBits, trellis, tblen, 'cont','hard');

Calculating BERs with and Without Convolutional Code

In this section, we calculate two BERs for the sequence: one for when the convolutional code is used and one for when it is not. We'll use the biterr function in the Communications Toolbox for the calculations. Finally, we use the berawgn function to calculate the theoretical error for a non-coded AWGN channel with the specified parameters:

% Compute BER without convolutional coding
[numErrors_Sym_no_code, bitError_Sym_no_code] = biterr(xEnc,yBits)

% Compute BER with convolutional coding
[numErrors_with_code,bitError_with_code] = biterr(x(1:end-tblen),y(tblen+1:end))

% Compute theoretical BER for an AWGN channel with parameters of this
% channel
ber_theory = berawgn (EsNo, 'qam', M)
numErrors_Sym_no_code =

        1374


bitError_Sym_no_code =

    0.0172


numErrors_with_code =

     0


bitError_with_code =

     0


ber_theory =

    0.0168

Functions Used in this Example

For more information about any of the functions used in this example, click on the following links. All functions are in the Communications Toolbox unless otherwise noted.

  • randint - Creates a sequence of random bits.
  • poly2trellis - Converts a convolutional code generator polynomial to a trellis.
  • convenc - Encodes a sequence of bits using a convolutional code with a given trellis.
  • scatterplot - Generates a scatter plot to show a signal constellation.
  • qammod - Modulates a sequence using QAM.
  • eyediagram - Displays an eyediagram of a modulated sequence.
  • rcosine - Designs a raised cosine filter.
  • fvtool - Graphically shows a filter's impulse response, magnitude response, and several other representations. (Signal Processing Toolbox)
  • upsample - Upsamples a sequence by inserting zeros. (Signal Processing Toolbox)
  • filter - Filters a sequence using a given FIR or IIR filter. (Included in MATLAB)
  • awgn - Transmits data over an AWGN channel.
  • See this overview of channel features in the toolbox.
  • downsample - Downsamples a sequence. (Signal Processing Toolbox)
  • qamdemod - Demodulates a sequence that was modulated using QAM.
  • vitdec - Decodes a sequence using a Viterbi decoder.
  • biterr - Calculates the bit error rate of a sequence.
  • berawgn - Calculates the uncoded theoretical bit error rate over an AWGN channel without coding.

Additional Information

For more information on using the Communications Toolbox, see:

Additional Communications Toolbox Examples

Communications Toolbox Documentation

From the main Communications Toolbox page you can download a free 30-day trial, read the documentation and user stories, request more information, and get pricing information.