2016.04.26 – 05.03
个人理解笔记。(无通信基础且急躁,片面/错误概率大大的。已待纠正)
相应的word版笔记保存地址:[6] OFDM链路的误码率和OFDM符号的功率谱密度。
04.27
一个OFDM信号由用PSK或QAM调制的多个子载波组成 [2] 。
本笔记中的OFDM由16QAM调制的64个子载波(64点IFFT/FFT变换)组成;每次会有3个OFDM信号经过多径Rayleigh衰减信道(最大延迟为15);每个OFDM符号的保护间隔类型为循环前缀(长度为16)。见Beasy_16QAM_64subcarrier_ofdm_link.m中的描述(同[5] OFDM符号 OFDM符号的多径Rayleigh信道链路中的Nframe_ofdm_multi_channel_link.m文件)。
Beasy_16QAM_64subcarrier_ofdm_link.m
% Beasy_16QAM_64subcarrier_ofdm_link.m
% 生成ofdm信号:64个子载波,由16QAM调制;
% 为ofdm添加循环前缀保护间隔,ofdm在多径信道中传输;
% 解析经多径信道后的ofdm信号,得到最初的数据。
clear
% --------OFDM链路参数描述模块---------------------------------------------
% @@@@@@@@ OFDM符号相关参数 @@@@@@@@
Nbps = 4; % 对应16QAM调制
M = 2 ^ Nbps; % 16QAM调制只能调制16进制数
Nfft = 64; % Nfft点ifft/fft变换 - 子载波的个数
Ncp = Nfft / 4; % 循环前缀保护间隔长度 (Ncp=0时表示OFDM无保护间隔)
Nsym = Nfft + Ncp; % OFDM符号的长度
Ndata = Nfft; % OFDM符号中的携带数据部分的长度
Nframe = 3; % 每次发送Nframe个OFDM符号
% @@@@@@@@ 多径Rayleigh衰减信道相关参数 @@@@@@@@
PowerdB = [0 -8 -17 -21 -25]; % 信道抽头功率分布(dB)
Delay = [0 3 5 6 8]; % 信道延迟样本
Power = 10.^(PowerdB / 10); % 信道抽头功率分布(线性
Ntap = length(PowerdB); % 信道抽头数
Lch = Delay(end) + 1; % 信道长度
channel = (randn(1, Ntap) + 1i * randn(1, Ntap)).*sqrt(Power / 2);
h = zeros(1, Lch);
h(Delay+1) = channel; % 信道脉冲响应
% --------OFDM符号生成模块-------------------------------------------------
% @@@@@@@@ 生成Ndata * Nframe个M进制数据 @@@@@@@@
DataSource = randi([0, M - 1], 1, Ndata * Nframe);
% @@@@@@@@ 将数据映射为QAM符号(时-->频) @@@@@@@@
MapObj = modem.qammod(M);
ModSym = modulate(MapObj, DataSource);
% @@@@@@@@ QAM符号串转并 @@@@@@@@
ModSymSrl2PrlIndx = 1 : Ndata;
ModPrlSym = zeros(Nframe, Ndata);
for i = 1 : Nframe
ModPrlSym(i, :) = ModSym(ModSymSrl2PrlIndx);
ModSymSrl2PrlIndx = ModSymSrl2PrlIndx + Ndata;
end
% @@@@@@@@ 利用ifft变换生成OFDM符号 @@@@@@@@
OfdmSym = zeros(Nframe, Ndata);
for i = 1 : Nframe
OfdmSym(i, :) = ifft(ModPrlSym(i, :));
end
% @@@@@@@@ 为每个OFDM符号添加循环前缀保护间隔 @@@@@@@@
GIOfdmSymIndx = 1 : Nsym;
OfdmSym_GI = zeros(Nframe, Nsym);
for i = 1 : Nframe
OfdmSym_GI(i, :) = add_cp(Ncp, Ndata, OfdmSym(i,:));
end
% @@@@@@@@ 带循环前缀的OFDM符号并转串 @@@@@@@@
GIOfdmSrlSymIndx = 1 : Nsym;
OfdmSrlSym_GI = zeros(1, Nframe * Nsym);
for i = 1 : Nframe
OfdmSrlSym_GI(GIOfdmSrlSymIndx) = OfdmSym_GI(i, :);
GIOfdmSrlSymIndx = GIOfdmSrlSymIndx + Nsym;
end
% --------OFDM符号经过多径信道模块-----------------------------------------
y = conv(OfdmSrlSym_GI,h);
% --------OFDM符号解析模块---------------------------------------------
rvOfdmSrlSym_GI = y;
OfdmSymIndx = 1 : Ndata;
H = fft([h zeros(1, Nfft - Lch)]); % 信道频域响应
ChFrqRep = H(OfdmSymIndx);
% @@@@@@@@ 带循环前缀的OFDM符号串转并 @@@@@@@@
rvOfdmSym_GI = zeros(Nframe, Nsym);
GIOfdmSrlSymIndx = 1 : Nsym;
for i = 1 : Nframe
rvOfdmSym_GI(i, :) = rvOfdmSrlSym_GI(GIOfdmSrlSymIndx);
GIOfdmSrlSymIndx = GIOfdmSrlSymIndx + Nsym;
end
% @@@@@@@@ 去掉OFDM符号的循环前缀 @@@@@@@@
rvOfdmSym = zeros(Nframe, Ndata);
for i = 1 : Nframe
rvOfdmSym(i, :) = remove_cp(Ncp, Nsym, rvOfdmSym_GI(i, :));
end
% @@@@@@@@ 用fft变换将OFDM符号变为QAM符号(时-->频),并进行信道补偿 @@@@@@@@
rvModPrlSym = zeros(Nframe, Ndata);
for i = 1 : Nframe
temp = fft(rvOfdmSym(i, :));
rvModPrlSym(i, :) = temp./ChFrqRep;
end
% @@@@@@@@ QAM符号并转串 @@@@@@@@
rvModSym = zeros(1, Nframe * Ndata);
ModSymPrl2SrlIndx = 1 : Ndata;
for i = 1 : Nframe
rvModSym(ModSymPrl2SrlIndx) = rvModPrlSym(i, :);
ModSymPrl2SrlIndx = ModSymPrl2SrlIndx + Ndata;
end
rvData = qamdemod(rvModSym, M);
add_cp.m
function y = add_cp(Ngi, Ndata, OfdmSym) % 为OFDM添加保护间隔 - 循环前缀 if Ngi ~= 0 y = [OfdmSym(Ndata - Ngi + 1 : Ndata) OfdmSym(1 : Ndata)];
else
y = OfdmSym;
end
end
remove_cp.m
function y = remove_cp(Ngi, Nsym, OfdmSym)
% 去掉OFDM的保护间隔 - 循环前缀
if Ngi ~= 0
y = OfdmSym(Ngi + 1 : Nsym); % 丢掉OFDM符号的循环前缀
else
y = OfdmSym;
end
end
04.28
误码率。
这里指的是BER(Bit Error Rate,二进制误码率)。对于OFDM链路来说,是指发送N次Nframe个OFDM符号,有多少个bit错误。
Beasy_16QAM_64subcarrier_ofdm_link.m
% Beasy_16QAM_64subcarrier_ofdm_link.m
% 生成ofdm信号:64个子载波,由16QAM调制;
% 为ofdm添加循环前缀保护间隔,ofdm在多径信道中传输;
% 解析经多径信道后的ofdm信号,得到最初的数据并计算误码率。
clear
% --------OFDM链路参数描述模块---------------------------------------------
% @@@@@@@@ OFDM符号相关参数 @@@@@@@@
…
Nframe = 3; % 每次发送Nframe个OFDM符号
Neb = 0; % 在OFDM链路中传输数据时,发生位(bit)错误的数量(Neb,Number of error bits)
Ntb = 0; % 在OFDM链路中传输的总位(bit)数(Ntb,Number of total bits)
Niter = 1e5; % OFDM发送Niter次信息统计一次误码率BER
% @@@@@@@@ 多径Rayleigh衰减信道相关参数 @@@@@@@@
…
for berIndx = 1 : Niter % 每Niter统计一次OFDM链路的误码率
% --------OFDM符号生成模块-------------------------------------------------
…
% --------OFDM符号经过多径信道模块-----------------------------------------
channel = (randn(1, Ntap) + 1i * randn(1, Ntap)).*sqrt(Power / 2);
h = zeros(1, Lch);
h(Delay+1) = channel; % 信道脉冲响应
y = conv(OfdmSrlSym_GI,h);
% --------OFDM符号解析模块---------------------------------------------
…
rvData = qamdemod(rvModSym, M);
% -------- 误码 --------------------------------------------------
Neb = Neb + sum(sum(de2bi(rvData,Nbps) ~= de2bi(DataSource, Nbps)));
Ntb = Ntb + Ndata * Nframe * Nbps;
end
% 误码率
BER = Neb / Ntb;
运行Beasy_16QAM_64subcarrier_ofdm_link.m几次,BER都为0。
04.29
在信道模型非常准确的情况下,以上的OFDM链路几乎可以做到0误码。但在实际的OFDM链路下,还存在一些其它干扰。现往OFDM链路中加入AWGN模型,再来看误码率。
Beasy_16QAM_64subcarrier_ofdm_link.m
% Beasy_16QAM_64subcarrier_ofdm_link.m
% 生成ofdm信号:64个子载波,由16QAM调制;
% 为ofdm添加循环前缀保护间隔,ofdm在多径信道中传输;
% 解析经多径信道和AWGN后的ofdm信号,得到最初的数据并计算误码率。
clear
% --------OFDM链路参数描述模块---------------------------------------------
% @@@@@@@@ OFDM符号相关参数 @@@@@@@@
…
Niter = 1e5; % OFDM发送Niter次信息统计一次误码率BER
Target_neb = 500; % Neb的最大值
% AWGN相关参数
EbN0 = 0 : 5 : 20;
sigPow = 0; % 信号功率
% @@@@@@@@ 多径Rayleigh衰减信道相关参数 @@@@@@@@
…
% 本程序误码率相关文件
ber_file_name = 'OFDM_BER_Rayleigh_CP_16.dat';
fd = fopen(ber_file_name, 'w+');
% if awgnIndx == 0, 计算信号功率sigPow
% else 计算误码数Neb
for awgnIndx = 0 : length(EbN0) % 跟AWGN相关的循环
% 误码数和码数清0
Neb = 0;
Ntb = 0;
for berIndx = 1 : Niter % 每Niter统计一次OFDM链路的误码率
% --------OFDM符号生成模块-------------------------------------------------
% @@@@@@@@ 生成Ndata * Nframe个M进制数据 @@@@@@@@
….
% --------OFDM符号经过多径信道模块---------------------------------
channel = (randn(1, Ntap) + 1i * randn(1, Ntap)).*sqrt(Power / 2);
h = zeros(1, Lch);
h(Delay+1) = channel; % 信道脉冲响应
y = conv(OfdmSrlSym_GI,h);
% 为添加AWGN噪声测量信号能量
if awgnIndx == 0
y1 = y(1 : Nframe * Nsym);
sigPow = sigPow + y1 * y1';
continue;
end
% 添加 AWGN 噪声---------------------------------------------------
snr = EbN0(awgnIndx) + 10 * log10(Nbps * (Ndata / Nfft)); % 4.28式
noise_mag = sqrt((10.^(-snr / 10)) * sigPow / 2);
y_AWGN = y + noise_mag * (randn(size(y)) + 1i * randn(size(y)));
% --------OFDM符号解析模块-----------------------------------------
…
rvData = qamdemod(rvModSym, M);
% -------- 误码计算 --------------------------------------------------
Neb = Neb + sum(sum(de2bi(rvData,Nbps) ~= de2bi(DataSource, Nbps)));
Ntb = Ntb + Ndata * Nframe * Nbps;
if Neb > Target_neb, break;end % 误码率迭代到这里已经足够大了
end
% 误码率
BER = Neb / Ntb;
if awgnIndx == 0
sigPow = sigPow / (Nsym * Nframe * Niter); % 每个OFDM信号的能量 - 平均值
else
fprintf(fd, '%d\t%11.3e\n', EbN0(awgnIndx), BER);
if BER < 1e-6, break; end % 已经满意当前这个误码率
end
end
if fd ~= 0, fclose(fd);end
disp('Simulation is finished');
运行Beasy_16QAM_64subcarrier_ofdm_link.m,得到OFDM_BER_Rayleigh_CP_16.dat文件,其内容如下:
Figure 1. 每发送Niter * Nframe个OFDM符号的误码率
将误码率以曲线的形式绘制出来。
Beasy_16QAM_64subcarrier_ofdm_link.m
…
if fd ~= 0, fclose(fd);end
plot_ber(ber_file_name);
disp('Simulation is finished');
plot_ber.m
function plot_ber( file_name ) % 绘制OFDM链路的误码率曲线 % file_name中保存的是EbN0和误码率值对 EbN0_BER = load(file_name);
semilogy(EbN0_BER(:,1),EbN0_BER(:,2),'b-o');
grid on
legend('OFDM模拟链路的误码率曲线');
xlabel('EbN0[dB]'), ylabel('BER');
axis([EbN0_BER(1,1) EbN0_BER(end,1) 1e-5 1]);
end
运行Beasy_16QAM_64subcarrier_ofdm_link.m,得到下图
Figure 2. OFDM链路误码率与EbN0关系曲线图
该部分的matlab代码保存地址为:16QAM_64subcarrier_ofdm_link_BER。
05.03
完全根据书本附带的代码搬迁/验证过来的,甚至不懂什么是功率谱密度,更别说计算OFDM符号的功率谱密度了。
误码率是在OFDM链路的接收部分计算,而功率谱密度是在OFDM链路的发射部分计算。
在Beasy_16QAM_64subcarrier_ofdm_link.m中计算一次一个OFDM符号的功率谱密度。
Beasy_16QAM_64subcarrier_ofdm_link.m
% Beasy_16QAM_64subcarrier_ofdm_link.m
…
% 在OFDM链路的接收端计算误码率(BER);在OFDM链路的发送端计算一个OFDM符号的功率谱密度(PSD)。
clear
% --------OFDM链路参数描述模块---------------------------------------------
…
% OFDM功率谱密度相关参数
NXpsd = 2048;
Frc = (1 : NXpsd) - NXpsd / 2; % OFDM符号功率谱密度横坐标个数
NIFFTOvspl = NXpsd / 2; % ifft变换的QAM符号长度
NpsdCp = 2 * Nfft; % 求功率谱密度时循环前缀的长度
Nprxz = round((NXpsd - NIFFTOvspl - NpsdCp) / 3); % 在ofdm符号前面补0的个数
Nsuxz = (NXpsd - NIFFTOvspl - NpsdCp) - Nprxz; % 在ofdm符号后面补0的个数
% if awgnIndx == 0, 计算信号功率sigPow
% else 计算误码数Neb
for awgnIndx = 0 : length(EbN0) % 跟AWGN相关的循环
% 误码数和码数清0
Neb = 0;
Ntb = 0;
for berIndx = 1 : Niter % 每Niter统计一次OFDM链路的误码率
% --------OFDM符号生成模块-------------------------------------------------
…
% ++++++++ 计算一次一个OFDM符号的PSD - 过采样++++++++
if awgnIndx == 0
IFFT_Oversample = [ModPrlSym(i, 1 : end / 2) zeros(1, NIFFTOvspl - Ndata) ModPrlSym(i, end / 2 + 1 : end)];
PsdOfdmSym = ifft(IFFT_Oversample);
PsdOfdmSymCP = add_cp(NpsdCp, PsdOfdmSym);
PsdOfdmSymCPAddZros = [zeros(1, Nprxz) PsdOfdmSymCP zeros(1, Nsuxz)]; % 过采样
OfdmSymComput = 20 * log10(abs(fft(PsdOfdmSymCPAddZros)));
OfdmSymPSDy = fftshift(OfdmSymComput) - max(OfdmSymComput);
end
% @@@@@@@@ 利用ifft变换生成OFDM符号 @@@@@@@@
…
end
% 误码率
…
end
if fd ~= 0, fclose(fd);end
% 绘制OFDM链路接收端的误码率曲线
figure(1); clf
plot_ber(ber_file_name);
% 绘制OFDM链路发射端的PSD曲线
figure(2); clf
plot(Frc,OfdmSymPSDy,'b');grid on
xlabel('frequency[Hz]'); ylabel('PSD[dB]'); axis([Frc([1 end]) -100 0]);
disp('Simulation is finished');
add_cp.m
function y = add_cp(Ngi, OfdmSym)
% 为OFDM添加保护间隔 - 循环前缀
if Ngi ~= 0
y = [OfdmSym(end - Ngi + 1 : end) OfdmSym];
else
y = OfdmSym;
end
end
remove_cp.m
function y = remove_cp(Ngi, OfdmSym)
% 去掉OFDM的保护间隔 - 循环前缀
if Ngi ~= 0
y = OfdmSym(Ngi + 1 : end); % 丢掉OFDM符号的循环前缀
else
y = OfdmSym;
end
end
运行Beasy_16QAM_64subcarrier_ofdm_link,得到在发射端的一个OFDM符号的功率谱密度:
Figure 3. OFDM链路发射端一个OFDM符号的功率谱密度(PSD)图
上图的横坐标值并不代表OFDM符号的频率值,它只是为了绘制纵坐标而出现的 ¬—— 它的个数跟纵坐标值的个数相同。
含OFDM链路接收端误码率(BER)和发送端一个OFDM符号功率谱密度(PSD)的matlab源码笔记地址为:16QAM_64subcarrier_ofdm_link_BER&PSD。
[2016.05.02 - 20:47]