本人最近搞懂了OFDM的一些知识,便给本章取名为第7章——OFDM信道估计、均衡。本文所有代码下载地址是:123kevin456/OFDM-
OFDM书籍随便一查有非常多,缺少均衡、同步相关的代码实现,便难体会一些通信过程。我最近正在看的两本OFDM书籍,都是理论+代码结合来讲解,推荐给你。
1、《MIMO-OFDM无线通信技术及MATLAB实现》
此书最大特点便是有MATLAB源代码下载!下载!!下载!!!但由于代码版本可能老旧,因此需要偶尔改改代码适应新的MATLAB版本才可以正常运行。我试过OFDM的几个代码,结果还是不错的。
2、《Simulation and Software Radio for Mobile Communications》
此书我目前还没有发现中文版本,因此直接看的英文版本。这本书介绍了PSK调制、OFDM、CDMA、多址接入协议等内容的仿真,均有MATLAB源代码。
就OFDM来说,第1本书比第2本书,难度更大,且在均衡、同步,比如载波同步、位定时同步等内容,讲得更加详细。由于难度太大,我已琢磨许久,偶有所得,便在此更新。
因此,本次文章后面分享的MATLAB可运行代码主要参考是第2本书。
关于OFDM原理的基本介绍,知乎上的 @子木在其《OFDM完整仿真过程及解释(MATLAB)》 已经介绍非常清楚了,本系列文章主要是作为补充,尤其是将OFDM经过瑞利信道时的误码率性能仿真,以及如何通过信道估计来对信道进行补偿(compensation)。
在这次的OFDM仿真中,为方便调试程序,首先做如下假设:
(1)调制:采用QPSK调制,可自己编写,也可以使用系统自带的pskmod函数;
(2)信道编码:一般采用(2,1,7)卷积码,本次仿真无编码;
(3)没有上载波与下载波过程,同时也没有发送端的上采样,接收端的匹配滤波,寻找最佳采样点过程;
待最基本的程序仿真正确后,再对其加入相应模块,以做完善。
本次仿真主要集中在将OFDM信号经过瑞利信道,是否有均衡,来观察误码率情况。
当然,在经过瑞利信道之前,不妨先测试一下OFDM在高斯白噪声信道下的传输。代码无误后,再将信道改成瑞利衰落信道。
先给出主要代码,各个函数的代码附在最后:
%%%%%%%%%%%%%%%%%%%%% OFDM仿真 %%%%%%%%%
%%%%%%%%%%%%%%%%%%%%% ofdm.m %%%%%%%%%
%%%%%%%%% date:2020年10月11日 author:飞蓬大将军 %%%%%%%%%%
%%%%%程序说明
%%%高斯白噪声信道下OFDM传输
%%%% 仿真环境
%软件版本:MATLAB R2019a
%********************** 程序主体 ************%
%%%%%%%%%%%%%%%%%%%%% 参数设置 %%%%%%%%%%%%%%%%%%%
para = 128; %Number of parallel channel to transmit
fftlen = 128; %FFT length
noc = 128; %Number of carrier
nd = 6; %Number of information OFDM symbol for one loop
ml = 2; %Modulation:QPSK
sr = 250000; %Symbol rate 符号速率
br = sr.*ml; %Bit rate per carrier
gilen = 32; %length of guard interval
ebn0_temp = 3:1:10;
for kkk = 1:length(ebn0_temp)
ebn0 = ebn0_temp(kkk); %Eb/No
% ebn0 = 8; %Eb/No
nloop = 10000; %Number of sumulation loops
noe = 0; %Number of error data
nod = 0; %Number of transmitted data
eop = 0; %Number of error packet
nop = 0; %Number of transmitted packet
for iii = 1:nloop
%%%%%%%%%%%%%%%%% 发射机 %%%%%%%%%%%%%%%%%%%
seldata = rand(1,para*nd*ml)>0.5; %串行数据
paradata = reshape(seldata,para,nd*ml); %串并转换
[ich,qch] = qpskmod(paradata,para,nd,ml); %调制
kmod = 1/sqrt(2);
ich1 = ich.*kmod;
qch1 = qch.*kmod;
%%%%%%%%%%%% IFFT %%%%%%%%%%
x = ich1 + qch1 *1j;
spow1 = sum(sum(ich1.^2+ qch1.^2))/nd./para;
y = ifft(x);
ich2 = real(y);
qch2 = imag(y);
spow2 = sum(sum(ich2.^2+ qch2.^2))/nd./para;
%%%%%%%% 添加保护间隔 %%%%%%%%
[ich3,qch3] = giins(ich2,qch2,fftlen,gilen,nd);
fftlen2 = fftlen + gilen;
% figure(1);
% plot(ich3,'-+');
% hold on;
%**************** Attenuation Calculation **************
spow = sum(ich3.^2+ qch3.^2)/nd./para;
attn = 0.5*spow*sr/br*10.^(-ebn0/10);
attn = sqrt(attn);
%%%%%%%%%%%%%%%%%%%%%%% 接收机 *************%%%%%%
%%%%%%%%%%% AWGN addition *******************
[ich4,qch4] = comb(ich3,qch3,attn);
% plot(ich4,'-*');
%%%%%%%% 去掉保护间隔 %%%%%%%%
[ich5,qch5] = girem(ich4,qch4,fftlen2,gilen,nd);
%%%%%%%%%%%%%% FFT %%%%%%%%
rx = ich5 + qch5.*1i;
ry = fft(rx);
ich6 = real(ry);
qch6 = imag(ry);
%%%%%%%%%%%%%% demoluation %%%%%%%%%%%%%%
ich7 = ich6./kmod;
qch7 = qch6./kmod;
demodata = qpskdemod(ich7,qch7,para,nd,ml);
%%%%%%%%%%%%%% 并串转换 %%%%%%%%%
demodata1 = reshape(demodata,1,para*nd*ml);
%%%%%%%%%%%%%%% Bit Error Rate %%%%%%%%%%%
noe2 = sum(abs(demodata1-seldata));
nod2 = length(seldata);
%%%%cumulative the number of error and data in noe and nod
noe = noe + noe2;
nod = nod + nod2;
%%%计算PER
if noe2~=0
eop = eop +1;
% else
% eop = eop;
end
nop = nop + 1;
% fprintf('%f\t%e\t%d\n',iii,noe2/nod2,eop);
end
%************** output result *************
per = eop/nop;
ber = noe/nod;
fprintf('%f\t%e\t%e\t%d\t\n',ebn0,ber,per,nloop);
fid = fopen('BERofdm.dat','a');
fprintf(fid,'%f\t%e\t%e\t%d\t\n',ebn0,ber,per,nloop);
fclose(fid);
end
%************************* 画误码率曲线进行对比 ******************%
% ebn0 = 3:1:10;
% awgn_theory = [0.0228784075610853,0.0125008180407376,0.00595386714777866,0.00238829078093281,0.000772674815378444,0.000190907774075993,3.36272284196176e-05,3.87210821552205e-06];
% awgn_no_compensation = [3.698496e-02,2.254329e-02,1.226654e-02,5.823633e-03,2.305339e-03,7.492187e-04,1.757812e-04,3.170573e-05];
% rayleign_one_path_theory = [0.125000000000000,0.100000000000000,0.0833333333333333,0.0714285714285715,0.0625000000000000,0.0555555555555556,0.0500000000000000,0.0454545454545455];
%
% semilogy(ebn0,awgn_theory,'-*',ebn0,awgn_no_compensation,'-+');
% xlabel('比特信噪比');
% ylabel('误码率');
% title('不同信噪比下误码率仿真曲线');
% legend('理论曲线','实验曲线');
% grid on;
%*********** 高斯白噪声下的OFDM采用QPSK调制的误码率值 **********%
%%%%%%%%%%%%%%%%%%%%%%% 理论值 **************%
%%%%%%%%%%%%% EbN0(dB) 误码率
%%%%%%%%%%%%% 3 0.0228784075610853
%%%%%%%%%%%%% 4 0.0125008180407376
%%%%%%%%%%%%% 5 0.00595386714777866
%%%%%%%%%%%%% 6 0.00238829078093281
%%%%%%%%%%%%% 7 0.000772674815378444
%%%%%%%%%%%%% 8 0.000190907774075993
%%%%%%%%%%%%% 9 3.36272284196176e-05
%%%%%%%%%%%%% 10 3.87210821552205e-06
%%%%%%%%%%%%%%%%%%%%% 实验值 *******%%%%%%%%%%%%
%%%%%%%%%%%%% EbN0(dB) 误码率 误包率 循环次数
% % % % % % % % % 3.000000 3.698496e-02 1.000000e+00 10000
% % % % % % % % % 4.000000 2.254329e-02 1.000000e+00 10000
% % % % % % % % % 5.000000 1.226654e-02 1.000000e+00 10000
% % % % % % % % % 6.000000 5.823633e-03 9.999000e-01 10000
% % % % % % % % % 7.000000 2.305339e-03 9.709000e-01 10000
% % % % % % % % % 8.000000 7.492187e-04 6.837000e-01 10000
% % % % % % % % % 9.000000 1.757812e-04 2.384000e-01 10000
% % % % % % % % % 10.000000 3.170573e-05 4.780000e-02 10000
%%%%%%%%%%%%%%%%% 结论 %%%%%%%%%%%%%%%%%%%%
%完成了OFDM经过高斯白噪声信道的误码率仿真
%由于OFDM中引入保护间隔,是一种冗余信息,因此相比于理论误码率曲线有10*log(160/128)=0.969dB的损失
%对应到误码率曲线上,即本实验误码率曲线相比理论误码率曲线向右平移0.969dB
%2020年11月7日
在上面的代码中,注意两点:
(1)当信号经过FFT之后,或者IFFT之后,功率会发生变化,这是为什么呢?功率的数量关系如何?
由N点DFT的表达式,知: X ( k ) = ∑ n = 0 N − 1 x ( n ) e − j 2 π N k n , k = 0 , 1 , ⋯ , N − 1 X\left( k \right) = \sum\limits_{n = 0}^{N - 1} {x\left( n \right){e^{ - j\frac{{2\pi }}{N}kn}}} ,k = 0,1, \cdots ,N - 1 X(k)=n=0∑N−1x(n)e−jN2πkn,k=0,1,⋯,N−1
为理解信号经过FFT前后功率发生的变化,联想到概率论中的公式: D ( a x + b y ) = a 2 D ( x ) + b 2 D ( y ) D\left( {ax + by} \right) = {a^2}D\left( x \right) + {b^2}D\left( y \right) D(ax+by)=a2D(x)+b2D(y)
因为 E { x ( 0 ) 2 } = E { x ( 1 ) 2 } = ⋯ = E { x ( N − 1 ) 2 } E\left\{ {x{{\left( 0 \right)}^2}} \right\} = E\left\{ {x{{\left( 1 \right)}^2}} \right\} = \cdots = E\left\{ {x{{\left( {N - 1} \right)}^2}} \right\} E{x(0)2}=E{x(1)2}=⋯=E{x(N−1)2}
所以有: E { X ( k ) 2 } = N ∙ E { x ( 0 ) 2 } E\left\{ {X{{\left( k \right)}^2}} \right\} = N \bullet E\left\{ {x{{\left( 0 \right)}^2}} \right\} E{X(k)2}=N∙E{x(0)2}
即信号经过FFT之后,功率变为原来的N倍。同理可以得到,信号经过IFFT后,功率变为原来的 1 N \frac{1}{N} N1 。
这样你便能理解ofdm.m代码中,发送端在经过IFFT后,才计算信号功率spow,再根据信噪比和spow,去计算相应的噪声功率。
(2)仿真曲线相比于理论曲线看起来是向右平移的关系,所以是平移的关系码?如果是平移,那到底是平移了多少dB呢?
由于在本次仿真中,所有子载波采用相同的调制方式。当然,没有规定就说所有的子载波一定要采用相同的调制方式。
因此,既然子载波是采用相同的QPSK调制方式,那么理论线就是QPSK的误码率曲线,QPSK和2PSK的误码率曲线是一样的。
如果对QPSK和2PSK的误码率曲线是否一致,还未想清楚的话,请点击《陈老湿:问答交流1:采用过采样的原因》,看问题2的解释。
结合《第二章:线性分组码》中讲过信息比特与信道比特的关系。在上面的代码中,由于加入了保护间隔,保护间隔是采用循环前缀(Cyclic Prelix,CP)方式,即将OFDM符号中的后一部分进行复制,加入到OFDM前面,构成一个完整的OFDM符号。
复制的这一段信号,占用了功率资源,没有用来发送新的信息比特,因此便有了功率损失。注意到子载波数目是128,CP长度是32,即CP长度占整个OFDM符号长度的1/5。
举个数值例子方便你理解,本来你有1w的功率用来发送1个比特,现在你只能有0.8w用来发这个比特了,因为还有0.2w要用去给CP了。
所以相比下来会损失:10log10(1)-10log10(4/5)=0.9691dB。对应到本次实验中,即实验曲线会相比理论曲线有向右平移0.9691dB。
你也可以根据《第二章:线性分组码》中Eb/N0与SNR的换算关系,来将实验曲线进行向左平移0.9691dB,便可发现二者相近了。
那么问题来了,CP有什么用呢?CP的长度如何取呢?(思考几秒钟?)
在多径信道中,当信号带宽大于无线信道的相干带宽时,链路会受到多径衰落的影响,从而造成ISI(符号间干扰)。插入CP,可以避免ISI。当然,这要求CP的长度要大于最大多径时延扩展。
上面这段话,我不知道你看着是什么感觉?书上都是这么写的,以及上课老师也是这么讲的?但都没有说清楚为什么CP长度大于最大多径时延扩展,便可以避免ISI。
最大多径时延扩展,可以理解对一个符号最晚到达路径相对于最早到达路径的时间差。
下面从数学表达式来帮助你理解,这需要你有循环卷积和线性卷积的概念。
如图4.14所示,如果保护间隔长度小于最大多径时延扩展,对于前一个OFDM符号的尾部已经影响到下一个OFDM符号的前端,这样就造成ISI。在实际中,可能会出现符号定时偏差(STO),STO会使OFDM信号的前端和FFT窗的起始点不一致。
如图4.15所示,若CP的长度大于最大多径时延扩展,也可能会出现ISI和ICI(子载波干扰),这取决于FFT的起始点的定时。如果FFT窗的起始点早于时延的前一个符号末端,则会出现ISI,如果FFT窗的起始点晚于符号的起始点,既会出现ISI,又会出现ICI。
因此,在本次的AWGN信道仿真中,如果不加入保护间隔,也是可以的,加入CP对AWGN信道来说没起到实际的用处。如果你把保护间隔去掉,便就可以得到误码率的实验曲线和理论曲线非常接近了。
因此,当保护间隔采用循环前缀(Cyclic Prelix,CP)方式时,即将OFDM符号中的后一部分进行复制,加入到OFDM前面,构成一个完整的OFDM符号,保证了发射采样和信道采样的循环卷积性质。
注意到循环卷积的理解,下一节将根据循环卷积原理来对信道进行补偿。
至此,已经讲完了CP的作用,以及怎么取。
CP在高斯白噪声信道下起不到作用,那么如何看到OFDM在多径瑞利信道传输,CP发挥作用呢?
我们便首先要知道瑞利信道如何进行建模与仿真,之后便可以看到均衡对于信道补偿的作用。我们下次再讲。
也欢迎读者朋友就相关技术问题与我交流,一起学习,共同进步。请你也别忘了把这篇文章分享给你身边正在学习通信专业的同学们,也许能够帮到Ta。这是《陈老湿·通信MATLAB》仿真的第7章,期待下次更新见!