High BER for QAM

18 views (last 30 days)
Muath
Muath on 8 Sep 2024
Edited: Muath on 9 Sep 2024
I am facing a problem here. I tried to implement a simulation for QAM from the scratch without using qammod, but the error rate seems to be so high, to the point I think the received symbols is just random. Can someone help with the code:
% 16-QAM Modulation and Demodulation with carrier 1 GHz.
clear all
close all
clc
% QAM Parameters:
M = 16; % Order of Modulation "16-QAM"
bits_symbol = log2(M); % Number of Bits per Symbol
fc = 1e9; % Carrier frequency
fs = 10*fc; % Sampling rate at least 4 to 10 times fc
num_symbol = 250; % Number of Symbols to be transmitted
t = (0:1/fs:num_symbol*(1/fs)-1/fs); % Time vector
% Random Input Generation:
input = randi([0,1], num_symbol, bits_symbol);
% Constellation Mapping:
constellation = (-sqrt(M)+1:2:sqrt(M)-1); %Constellation [-3, -1, 1, 3]
% Map the symbols:
I = constellation(bi2de(input(:,1:bits_symbol/2),'left-msb')+1); % In-phase component
Q = constellation(bi2de(input(:, bits_symbol/2+1:end),'left-msb')+1); % Quadrature component
% Modulated signal:
Tx = I .* cos(2*pi*fc*t) + Q .* sin(2*pi*fc*t);
% Tx = awgn(Tx, 20, 'measured'); % Adding noise for testing
% Demodulation:
Rx_I = 2 * Tx .* cos(2*pi*fc*t); % In-phase demodulation
Rx_Q = 2 * Tx .* sin(2*pi*fc*t); % Quadrature demodulation
% Lowpass filter:
Rx_I_f = lowpass(Rx_I, fc/2, fs);
Rx_Q_f = lowpass(Rx_Q, fc/2, fs);
% Symbol decision (finding the closest constellation point):
I_org = zeros(num_symbol, 1);
Q_org = zeros(num_symbol, 1);
for x = 1:num_symbol
[~, I_org(x)] = min(abs(Rx_I_f(x) - constellation'));
[~, Q_org(x)] = min(abs(Rx_Q_f(x) - constellation'));
end
% Combine I_org and Q_org to get the original symbols:
output = [de2bi(I_org-1, bits_symbol/2, 'left-msb'), de2bi(Q_org-1, bits_symbol/2, 'left-msb')];
% Calculate the Bit Error Rate (BER):
[~, BER] = biterr(input, output);
fprintf('Bit Error Rate (BER): %f\n', BER);

Accepted Answer

Shashi Kiran
Shashi Kiran on 9 Sep 2024
I understand that you are experiencing poor performance with the Bit Error Rate (BER) while implementing the 16-QAM modulation scheme.
After analysing your code, here are my observations and suggestions for improvement.
  • The time vector should cover the entire duration of all symbols, not just one symbol. You can correct it in the following way
symbol_duration = 1 / fc; % Duration of one symbol
t = (0:1/fs:(num_symbols * symbol_duration) - 1/fs);
  • The In-phase and Quadrature components should be applied individually to each symbol. I and Q should be applied to each symbol separately.
samples_per_symbol = fs * symbol_duration;
Tx = zeros(1, length(t));
for k = 1:num_symbols
start_index = (k - 1) * samples_per_symbol + 1;
end_index = k * samples_per_symbol;
Tx(start_index:end_index) = ...
I(k) * cos(2 * pi * fc * t(start_index:end_index)) + ...
Q(k) * sin(2 * pi * fc * t(start_index:end_index));
end
  • The demodulated values are sampled incorrectly, as they are directly indexed by x, which does not account for the samples per symbol.
for k = 1:num_symbols
sample_index = round((k - 0.5) * samples_per_symbol);
[~, I_org(k)] = min(abs(Rx_I_f(sample_index) - constellation'));
[~, Q_org(k)] = min(abs(Rx_Q_f(sample_index) - constellation'));
end
With these corrections, you can achieve the desired Bit Error Rate (BER) for 16-QAM, which means a BER of zero in the absence of noise and a waterfall curve as the Signal-to-Noise Ratio (SNR) increases.
Here is the full code after corrections with SNR of 8dB.
clear all
close all
clc
% QAM Parameters:
M = 16; % Order of Modulation "16-QAM"
bits_per_symbol = log2(M); % Number of Bits per Symbol
fc = 1e9; % Carrier frequency
fs = 10 * fc; % Sampling rate at least 4 to 10 times fc
num_symbols = 250; % Number of Symbols to be transmitted
symbol_duration = 1 / fc; % Duration of one symbol
samples_per_symbol = fs * symbol_duration; % Number of samples per symbol
t = (0:1/fs:(num_symbols * symbol_duration) - 1/fs); % Time vector
% Random Input Generation:
input = randi([0, 1], num_symbols, bits_per_symbol);
% Constellation Mapping:
constellation = (-sqrt(M) + 1:2:sqrt(M) - 1); % Constellation [-3, -1, 1, 3]
% Map the symbols:
I = constellation(bi2de(input(:, 1:bits_per_symbol/2), 'left-msb') + 1); % In-phase component
Q = constellation(bi2de(input(:, bits_per_symbol/2+1:end), 'left-msb') + 1); % Quadrature component
% Modulated signal:
Tx = zeros(1, length(t));
for k = 1:num_symbols
start_index = (k - 1) * samples_per_symbol + 1; % Start index for the k-th symbol
end_index = k * samples_per_symbol; % End index for the k-th symbol
Tx(start_index:end_index) = ...
I(k) * cos(2 * pi * fc * t(start_index:end_index)) + ...
Q(k) * sin(2 * pi * fc * t(start_index:end_index));
end
Tx = awgn(Tx, 8, 'measured'); % Uncomment to add noise for testing
% Demodulation:
Rx_I = 2 * Tx .* cos(2 * pi * fc * t); % In-phase demodulation
Rx_Q = 2 * Tx .* sin(2 * pi * fc * t); % Quadrature demodulation
% Lowpass filter:
Rx_I_f = lowpass(Rx_I, fc / 2, fs);
Rx_Q_f = lowpass(Rx_Q, fc / 2, fs);
% Symbol decision (finding the closest constellation point):
I_org = zeros(num_symbols, 1);
Q_org = zeros(num_symbols, 1);
for k = 1:num_symbols
sample_index = round((k - 0.5) * samples_per_symbol); % Sample in the middle of the symbol period
[~, I_org(k)] = min(abs(Rx_I_f(sample_index) - constellation'));
[~, Q_org(k)] = min(abs(Rx_Q_f(sample_index) - constellation'));
end
% Combine I_org and Q_org to get the original symbols:
output = [de2bi(I_org - 1, bits_per_symbol/2, 'left-msb'), de2bi(Q_org - 1, bits_per_symbol/2, 'left-msb')];
% Calculate the Bit Error Rate (BER):
[~, BER] = biterr(input, output);
fprintf('Bit Error Rate (BER): %f\n', BER);
Bit Error Rate (BER): 0.021000
  1 Comment
Muath
Muath on 9 Sep 2024
Edited: Muath on 9 Sep 2024
The code works flawlessly. Thanks a lot!

Sign in to comment.

More Answers (1)

Ruchika Parag
Ruchika Parag on 9 Sep 2024
Hi Muath, when developing a QAM (Quadrature Amplitude Modulation) simulation from scratch, it's crucial to focus on several key elements to ensure the modulation and demodulation processes are correctly executed. To achieve lower error rate, here are a few things that can be modified in your code :
  1. Increased SNR: The SNR can been set to 15 dB. You can adjust this value further based on your requirements. A higher SNR generally results in a lower BER.
  2. AWGN Noise Addition: The noise is added to the transmitted signal to simulate a more realistic communication channel.
  3. Improved Lowpass Filtering: The lowpass filter is retained but ensure that it effectively removes noise while preserving the signal.
Following is the modified code to achieve lower BER :
clear all;
close all;
clc;
% QAM Parameters:
M = 16; % Order of Modulation "16-QAM"
bits_symbol = log2(M); % Number of Bits per Symbol
fc = 1e9; % Carrier frequency
fs = 10 * fc; % Sampling rate at least 4 to 10 times fc
num_symbol = 250; % Number of Symbols to be transmitted
t = (0:1/fs:num_symbol*(1/fs)-1/fs); % Time vector
% Random Input Generation:
input = randi([0, 1], num_symbol, bits_symbol);
% Constellation Mapping:
constellation = (-3:2:3) + 1i * (-3:2:3)'; % Create a 4x4 grid for 16-QAM
constellation = constellation(:); % Reshape to a column vector
% Map the symbols:
I_bits = input(:, 1:bits_symbol/2); % In-phase bits
Q_bits = input(:, bits_symbol/2+1:end); % Quadrature bits
I_decimal = bi2de(I_bits, 'left-msb'); % Convert in-phase bits to decimal
Q_decimal = bi2de(Q_bits, 'left-msb'); % Convert quadrature bits to decimal
I = constellation(I_decimal + 1); % In-phase component
Q = constellation(Q_decimal + 1); % Quadrature component
% Modulated signal:
Tx = I .* cos(2 * pi * fc * t) - Q .* sin(2 * pi * fc * t);
% Add AWGN noise
SNR_dB = 15; % Increase SNR to reduce BER
Tx_power = mean(abs(Tx).^2);
noise_power = Tx_power / (10^(SNR_dB/10));
noise = sqrt(noise_power) * randn(size(Tx)); % Additive white Gaussian noise
Rx = Tx + noise; % Received signal
% Demodulation:
Rx_I = 2 * Rx .* cos(2 * pi * fc * t); % In-phase demodulation
Rx_Q = 2 * Rx .* sin(2 * pi * fc * t); % Quadrature demodulation
% Lowpass filter:
Rx_I_f = lowpass(Rx_I, fc/2, fs);
Rx_Q_f = lowpass(Rx_Q, fc/2, fs);
% Symbol decision (finding the closest constellation point):
I_org = zeros(num_symbol, 1);
Q_org = zeros(num_symbol, 1);
for x = 1:num_symbol
[~, I_org(x)] = min(abs(Rx_I_f(x) - real(constellation)));
[~, Q_org(x)] = min(abs(Rx_Q_f(x) - imag(constellation)));
end
% Combine I_org and Q_org to get the original symbols:
output_I = de2bi(I_org - 1, bits_symbol/2, 'left-msb'); % Convert in-phase to bits
output_Q = de2bi(Q_org - 1, bits_symbol/2, 'left-msb'); % Convert quadrature to bits
output = [output_I, output_Q]; % Combine in-phase and quadrature bits
% Calculate the Bit Error Rate (BER):
[~, BER] = biterr(input, output);
fprintf('Bit Error Rate (BER): %f\n', BER);
Bit Error Rate (BER): 0.480000
By increasing the SNR and ensuring that your modulation and demodulation processes are correctly implemented, you should see a reduction in the Bit Error Rate.
  1 Comment
Muath
Muath on 9 Sep 2024
Hi Ruchika,
Thank you for your feedback. I considered removing the noise first and tried to implement the transmitting and receiving part correctly. That is why the noise is commented out in my code. So, the noise part is irrelevant for now.
Focusing on the implementation, I reviewed your modification to the code. However, I couldn’t understand the following part. You converted the constellation mapping to a 16x1 matrix which has the values:
-3-3j, -3-1j,…, 3+3j
which is fine. But when you divide the symbol between I and Q, it means 2 bits for each. The possible values for 2 bits are 0, 1, 2, 3, and by doing the adjustment to be used as an index, we add 1 which will make them 1, 2, 3, 4.
This means it will only use the first four values in the constellation map:
-3 - 3j, -3 - 1j, -3 + 1j, -3 + 3j
So, what is the point of having 16 mapping points when you are only going to use four of them? Nonetheless, I ran the code and it resulted in an error most of the time, exactly at line 58:
output_I = de2bi(I_org - 1, bits_symbol/2, 'left-msb'); % Convert in-phase to bits
And when it ran properly, the BER was high, similar to the run result you have shown above.

Sign in to comment.

Products


Release

R2024a

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!