Feedforward equalizer (FFE) MMSE vs ZF: I get ZF performs better than MMSE, why is that?

47 views (last 30 days)
So I am doing some experiments on comparing MMSE and ZF performance. Here is the description of my problem setup:
Consider a pulse response p_tilde which is equal to:
p_tilde = [7.41283693728174e-06 -6.84231291007695e-05 -0.0443418886383774 -0.0151503353629903 -0.0272769624198551 -0.0177892552560615 -0.0203350692581077 -0.0163873767272264 -0.0164306282845029 -0.0142362825364328 -0.0136655824141384 -0.0121940540773202 -0.0114619811302311 -0.0104124216153988 -0.00964899540994174 -0.00899196802973862 -0.00818566902044469 -0.00762730911499311 -0.00699141833441100 -0.00648613497013486 -0.00591440709340248 -0.00553607986409091 -0.00505027582877288 -0.00474052280850206 -0.00430261543533403 -0.00402597225213157 -0.00364873023512323 -0.00342623983233682 -0.00309870405925617 -0.00287855285961599 -0.00259826586794989 -0.00242555114477522 -0.00216311155170253 -0.00201954752590112 -0.00187659490896144 -0.00169995749278872 -0.00160002957958732 -0.00143570523645728 -0.00137503899976676 -0.00125830844814117 -0.00119472748925794 -0.00110403717898079 -0.00103406206999471 -0.000958275964792411 -0.000911849041772657 -0.000845030004218205 -0.000816232809699085 -0.000667539573097499 -0.000648754798002034 -0.000553122573229280 -0.000521309202162978 -0.000365972657867558 -0.000427904780904760 -0.000303965793819752 -0.000299468601099196 -0.000225217170299876 -0.000159927160163347 -4.27943799247876e-05 5.77535188454308e-06 -1.60324544347288e-05 7.58340989099088e-05 4.90191180358249e-05 5.55787742344576e-05];
nu = length(p_tilde) - 1;
N = 1; % Number of dimensions per symbol
Nf = 30; % number of FFE taps
L = 1; % oversampling ratio
Ex = 1;
Ex_bar = Ex / N;
% First split p into blocks (columns) of L samples each
% Number of blocks:
n_blocks = 1 + ceil((length(p_tilde)-1)/L);
% Last block may require zero-padding
if (mod(length(p_tilde)-1,L) ~= 0)
n_missing_samples = L - mod(length(p_tilde)-1,L);
else
n_missing_samples = 0;
end
% Zero-pad and split:
p_0 = [p_tilde(1); zeros(L-1,1)];
p_split = [p_0 flipud(reshape([p_tilde(2:end) ...
zeros(1,n_missing_samples)], L, n_blocks-1))]
% Note: flipud is used to distribute the p_tilde samples as a CW
% commutator over the L branches of the polyphase decomposition
% Generate a Toeplitz matrix
nRows = Nf * L;
nCol = Nf + nu;
% Preallocate
P = zeros(nRows, nCol);
nZerosRight = nCol - (nu + 1);
nZerosLeft = 0;
nBlocks = Nf;
for iBlock = 1:nBlocks
P((iBlock-1)*L + 1 : (iBlock*L),:) = [zeros(L, nZerosLeft) p_split zeros(L, nZerosRight)];
nZerosLeft = nZerosLeft + 1;
nZerosRight = nZerosRight - 1;
end
[a,b]= max(abs(sampled_channel_coefficients_afe));
delta = b-1; % see page 21 at https://people.engr.tamu.edu/spalermo/ecen689/lecture7_ee689_eq_intro_txeq.pdf; here I assume no FFE pre-cursor
% Cross-correlation
R_Yx = Ex_bar * P * [zeros(delta,1); 1; zeros(Nf + nu - delta - 1, 1)]
% Auto-correlation
SNR_dB = 32.126534578279106;
SNR = 10^(SNR_dB/10); % Note this is not SNRmfb ("one-shot" bound), but simply the receiver biased SNR
noise_en_per_dim = Ex_bar/SNR
R_YY_mmse = (Ex_bar * P * P') + (L * noise_en_per_dim *eye(Nf*L))
R_YY_zf = (Ex_bar * P * P')
% MMSE
w_mmse = (inv(R_YY_mmse)*R_Yx)'
w_zf = (inv(R_YY_zf)*R_Yx)'
If you run the above code, you will get the following tap weights:
w_mmse = [-16.0334968779402 4.81345350767911 6.45455599790458 1.05531267863522 0.862544811465639 0.154226345074783 0.155009947755956 0.00415771541535660 0.0469282011013412 -0.00373585377344961 0.0126325827750029 0.00933571396627974 -0.00280141664202185 0.0401862503056695 -0.0156342273933104 0.00299196074830827 7.48619764623058e-05 0.00295098323035444 -0.0120788898062787 0.0136305155251233 -0.00262402928039840 0.0114544066067792 -0.00947152098371525 0.00218325999598388 -0.00962498002977390 0.00565329024769428 -0.00760605356156193 -0.00330735044519261 -0.00936301791652029 0.00463602328709300];
w_zf = [-22.5610583952446 7.69159287211292 11.2502110473252 0.475800804762431 0.177960155261523 -0.0564236911727865 0.0770702791028816 -0.0456116693343436 0.0481983891750188 -0.0136058797432089 0.00953080508217355 0.0111016906882175 -0.0107606798793674 0.0785469849940052 -0.0363898394254151 -0.00916857774920463 0.00864999071589379 0.00320710119648796 -0.0254101797448969 0.0258281970465915 -0.000640995082141195 0.0179348156324128 -0.0180090776779173 0.000496679609183903 -0.0144388420218252 0.0133258761460034 -0.0101177143789741 -0.00842776943416558 -0.0144975418511249 0.0119841389349415];
Now, we run the time domain analysis:
M = 4; % PAM4
N = 1e7; % number of symbols
sampled_channel_coefficients_afe = p_tilde;
tap_weights_ffe_zf = w_zf;
tap_weights_ffe_mmse = w_mmse;
% generate PAM4 data
data = randi([0, M-1], N, 1);
modData = real(pammod(data,M,0,'gray'));
% convolve with sampled_channel_coefficients_afe
signal_in = conv(modData, sampled_channel_coefficients_afe);
Ex = mean((abs(signal_in.^2)));
% SNR
SNR = 32.126534578279106; % dB
% AWGN channel
signal_in_awgn = awgn(signal_in, SNR, 'measured'); %- input signal to equalize
% no noise:
eyediagram(signal_in(N-3000:N-1000),3);
% no eq but with noise:
eyediagram(signal_in_awgn(N-3000:N-1000),3);
% equalized using ZF:
signal_eq_zf = conv(signal_in_awgn, tap_weights_ffe_zf, 'same');
tap_weights_ffe_zf = tap_weights_ffe_zf/max(abs(signal_eq_zf));
signal_eq_zf = conv(signal_in_awgn, tap_weights_ffe_zf, 'same');
eyediagram(signal_eq_zf(N-3000:N-1000),3);
% equalized using MMSE:
signal_eq_mmse = conv(signal_in_awgn, tap_weights_ffe_mmse, 'same');
tap_weights_ffe_mmse = tap_weights_ffe_mmse/max(abs(signal_eq_mmse));
signal_eq_mmse = conv(signal_in_awgn, tap_weights_ffe_mmse, 'same');
eyediagram(signal_eq_mmse(N-3000:N-1000),3);
% equalized using LMS:
eq = comm.LinearEqualizer('NumTaps', 30, 'Constellation', [-3, -1, 1, 3], 'InputDelay', 1);
[signal_eq_lms, error, tap_weights_ffe_lms] = eq(signal_in_awgn, modData);
eyediagram(signal_eq_lms(N-3000:N-1000),3);
And this is the eye of ZF equalized signal:
And this is the eye of MMSE equalized signal:
And finally this is the eye of LMS equalized signal:
As you can see, ZF is clearly better MMSE even the signal is in AWGN channel with a SNR = 32.126534578279106 dB.
So my question is, why this is the case? This is confusing me for a while so any input and help will be extremely helpful!

Answers (1)

vidyesh
vidyesh on 22 Jan 2024
Edited: vidyesh on 22 Jan 2024
Hello Zonghao Li,
In your simulation, the Zero Forcing (ZF) equalizer seems to surpass the Minimum Mean Square Error (MMSE) equalizer despite the presence of noise. MMSE typically excels in noisy scenarios by incorporating noise power into filter coefficient calculations to minimize total error. ZF, however, strictly focuses on channel inversion.
This unexpected result might stem from the channel's unique properties and the noise intensity. MMSE tends to outshine ZF under conditions of severe frequency selective fading or high noise levels. Yet, with moderate fading or low noise (high SNR) ZF could match or even exceed MMSE performance.
Furthermore, the number of taps in the Finite Impulse Response (FIR) filters could impact equalization. A higher tap count than the current 'Nf = 30' might improve both equalizers' effectiveness. For a more definitive performance assessment between ZF and MMSE, consider a Bit Error Rate (BER) comparison.
The following MATLAB Answers post shows a BER analysis of MMSE and ZF equalizers
Hope this helps

Categories

Find more on Design and Simulate SerDes Systems in Help Center and File Exchange

Products


Release

R2022a

Community Treasure Hunt

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

Start Hunting!