文章目录

  • 初识OFDM(六):从零开始的OFDM误码率仿真
  • 零.代码地址
  • 一. 加性高斯白噪声对OFDM误码率的影响
    • 1. 代码展示
    • 2. 代码分析
      • fftshift和ifftshift
      • 能量和信噪比问题
  • 二.瑞利信道对OFDM误码率的影响
    • 1. 代码展示
    • 2. 代码分析
      • 瑞利衰落信道是如何通过TDL模型仿真而成的
      • 线性卷积,循环卷积和均衡
      • 线性卷积输入和输出长度怎么不相等了?
  • 三. 一些还没有思考清楚的问题

初识OFDM(六):从零开始的OFDM误码率仿真

零.代码地址

https://github.com/liu-zongxi/OFDM_simulation

代码参考了https://zhuanlan.zhihu.com/p/385096476陈老湿的仿真,但各个函数我都有重新实现,希望写的更规范一些

一. 加性高斯白噪声对OFDM误码率的影响

1. 代码展示

该部分代码来自工程的OFDM_awgn.m

整个OFDM经历

生成一帧 →\rightarrow串并转换 →\rightarrow调制 →\rightarrowifftshift →\rightarrowifft →\rightarrow添加CP →\rightarrow并串转换 →\rightarrow计算能量,信噪比,添加AWGN →\rightarrow接收端串并转换 →\rightarrow去CP →\rightarrowFFT →\rightarrowFFTshift →\rightarrow解调 →\rightarrow并串转换

%-----------------------仿真OFDM---------------------------%
%-----------------------author:lzx-------------------------%
%% 设置参数
clear;clc;
Nk = 128;           % 子载波个数
Nfft = 128;          % fft长度
Nframe = 6;         % 一帧中有几个OFDM符号
M = 2;              % 调制符号所含比特
SR = 250000;        % 符号速率
BR = SR .* M;       % 比特率
NGI = 32;           % 保护间隔长度
EbN0s = 3:1:10;      % 信噪比
Nsym = Nfft+NGI;    % 系统长度
bers = zeros(1,length(EbN0s));
fprintf('EbN0 \t \t ber\t\t\t per\t\t\t nloop \t\t \n');
%% 函数主体for kk = 1:length(EbN0s)% rng('default')          % 初始化随机种子EbN0 = EbN0s(kk);nloop = 10000;          % 发送多少帧n_biterror = 0;         % 错误的数据n_bitdata = 0;          % 一共发送了多少数据n_packeterror = 0;      % 有多少错误帧n_packetdata = 0;       % 发送了多少帧for ii = 1:nloop% 生成一帧数据,串并转换,并QPSK,生成一帧frame_FDserial = rand(1,Nk*Nframe*M) > 0.5;     % 发送的是bitframe_FDparallel = reshape(frame_FDserial,Nk,Nframe*M);% 串并转换frame_mod = QPSKMod(frame_FDparallel,Nk,Nframe);     %调制% IFFTpower_FT = sum(sum(abs(frame_mod).^2))/Nk/Nframe;  % 计算下IFFT前的能量,FT表示频域frame_mod_shift = ifftshift(frame_mod);         % 频域归零frame_ifft = ifft(frame_mod_shift, Nfft);             % ifftpower_TD = sum(sum(abs(frame_ifft).^2))/Nk/Nframe; % 计算下IFFT前的能量,DT表示时域% 添加保护间隔frame_withGI = AddGI(frame_ifft, Nfft, NGI, Nframe, "CP");  % 添加保护间隔% 并串转换frame_TDserial = reshape(frame_withGI,1,Nsym*Nframe);x=1:1:160;% hold on;% plot(x, frame_TDserial(1:160),'b');% Channelpower_TDserial = sum(abs(frame_TDserial).^2)/Nsym/Nframe;   EsN0 = EbN0 + 10*log10(M);                                  % 根据信噪比计算噪声能量,幅值,然后加在信号上N0 = power_TDserial .* 10.^(-EsN0/10);noise_msg = sqrt(N0 / 2) .* (randn(size(frame_TDserial)) + 1j * randn(size(frame_TDserial)));frame_recieved = frame_TDserial + noise_msg;
%         attn = sqrt(0.5*power_TDserial*SR/BR*10.^(-EbN0/10));
%         noise_msg = attn .* (randn(size(frame_TDserial)) + 1j * randn(size(frame_TDserial)));
%         frame_recieved = frame_TDserial + noise_msg;% plot(x, noise_msg(1:160),'r');% hold off;% 接收端,串并转换frame_recieved_parallel = reshape(frame_recieved,Nsym,Nframe);% 去GIframe_noGI = RemoveGI(frame_recieved_parallel, Nfft, NGI);% FFTframe_recieved_FD_shift = fft(frame_noGI, Nfft);frame_recieved_FD = fftshift(frame_recieved_FD_shift);% QPSK解调frame_demod = QPSKDemod(frame_recieved_FD, Nk, Nframe);% 并串转换frame_output = reshape(frame_demod, 1, Nk*Nframe*M);% 计算errorn_biterror_tmp = sum(abs(frame_output-frame_FDserial));n_bitdata_tmp = length(frame_FDserial);n_biterror = n_biterror + n_biterror_tmp;n_bitdata = n_bitdata + n_bitdata_tmp;if n_biterror_tmp ~= 0n_packeterror = n_packeterror + 1;endn_packetdata = n_packetdata + 1;end% 计算在当前信噪比下的误码率per = n_packeterror/n_packetdata;ber = n_biterror/n_bitdata;bers(kk)=ber;fprintf('%f\t%e\t%e\t%d\t\n',EbN0,ber,per,nloop);
end
save("BERofdm.mat",'bers');
%% 画图
% bers = load("BERofdm.mat").bers;
awgn_theory = [0.0228784075610853,0.0125008180407376,0.00595386714777866,0.00238829078093281,0.000772674815378444,0.000190907774075993,3.36272284196176e-05,3.87210821552205e-06];
semilogy(EbN0s,awgn_theory,'-*',EbN0s,bers,'-+');
xlabel('比特信噪比');
ylabel('误码率');
title('不同信噪比下误码率仿真曲线');
legend('理论曲线','实验曲线');

2. 代码分析

这个代码主要有这么几点是值得注意的

fftshift和ifftshift

  1. ifftshift和fftshift到底是什么呢?

简单来说,ifftshift是把矩阵做一个向左的长度为矩阵一半的循环位移,fftshift则是把矩阵做一个向右的长度为矩阵一半的循环位移。

  1. 作用是什么呢?

当做ifft时,我们希望ifft窗对准的是[−Nk2,Nk2][-\frac{Nk}{2},\frac{Nk}{2}][2Nk,2Nk]这样一段信号,而我们把他放在matlab中进行仿真时,下标则是[1,Nk][1,Nk][1,Nk].因此,这就对应错了啊,因此我们要把前后倒置一下,这才满足理论上的要求。相应的,做完fft后,我们又要把结果恢复到[1,Nk][1,Nk][1,Nk],这样才方便和输入进行对比。

  1. 如何做?

注意是顺序是先做ifftshift再做ifft,而接收端是先fft再做fftshift,原因上面已经说的很清楚啦。

能量和信噪比问题

  1. IFFT和FFT前后的能量变化

想象一下FFT或是IFFT的公式.

IFFT中每一个x[n]x[n]x[n] 是Nk个1NkX[k]\frac{1}{Nk}X[k]Nk1X[k]组成的,因此能量变为1Nk2×Nk=1Nk\frac{1}{Nk^2}\times Nk=\frac{1}{Nk}Nk21×Nk=Nk1

FFT 中每一个X[k]X[k]X[k]是Nfft个x[n]x[n]x[n]组成的,因此能量变为NfftNfftNfft

能量是会产生变化的,而噪声是在信道时域中加上的,因此我们信噪比使用了时域的能量来计算。

  1. EsN0和EbN0的关系

https://blog.csdn.net/sinat_38151275/article/details/79869891可以参考这篇文章

总的来说,当经过数字调制后,一个符号包含了Npsk个比特那么
EsN0=EbN0=10log10(Npsk)EsN0=EbN0=10log_{10}(Npsk) EsN0=EbN0=10log10(Npsk)

  1. CP对信噪比的影响?

https://zhuanlan.zhihu.com/p/281601152,陈老湿在这里做了很多探讨,不过我更偏向于将CP不看做冗余

因此我的代码是这样的

        power_TDserial = sum(abs(frame_TDserial).^2)/Nsym/Nframe;

二.瑞利信道对OFDM误码率的影响

1. 代码展示

该部分代码来自工程的OFDM_fading.m

整个OFDM经历

生成一帧 →\rightarrow串并转换 →\rightarrow调制 →\rightarrowifftshift →\rightarrowifft →\rightarrow添加CP →\rightarrow并串转换 →\rightarrow经历信道衰落,和信道卷积→\rightarrow计算能量,信噪比,添加AWGN →\rightarrow接收端串并转换 →\rightarrow去CP →\rightarrowFFT →\rightarrowFFTshift →\rightarrow→\rightarrowFFT信道矩阵h→\rightarrow信道均衡→\rightarrow解调 →\rightarrow并串转换

%-----------------------用TDL仿真多径衰落-------------------%
%-----------------------author:lzx-------------------------%
%-----------------------date:2022年3月25日-----------------%
%% 设置参数
clear;clc;
Nk = 128;           % 子载波个数
Nfft = 128;          % fft长度
Nframe = 6;         % 一帧中有几个OFDM符号
Npsk = 2;              % 调制符号所含比特
M = 2^Npsk;          % 调制数
SR = 250000;        % 符号速率
BR = SR .* Npsk;       % 比特率
NGI = 32;           % 保护间隔长度
EbN0s = 0:2:20;      % 信噪比
Nsym = Nfft+NGI;    % 系统长度
bers = zeros(1,length(EbN0s));  % 误码率储存数组
PowerTDL_dB = [0 -8 -17 -21 -25];   % TDL中信道抽头的功率,dB为单位
Delay = [0 3 5 6 8];                % TDL中信道时延
PowerTDL = 10.^(PowerTDL_dB/10);    % TDL中信道抽头的功率
Nchannel=length(PowerTDL_dB);       % 信道抽头数
Tau_maxTDL = Delay(end)+1;          % 最大时延除以帧长,就是归一化的最大时延
fprintf('EbN0 \t \t ber\t\t\t per\t\t\t nloop \t\t \n');
%% 函数主体for kk = 1:length(EbN0s)% rng('default')          % 初始化随机种子EbN0 = EbN0s(kk);nloop = 10000;          % 发送多少帧n_biterror = 0;         % 错误的数据n_bitdata = 0;          % 一共发送了多少数据n_packeterror = 0;      % 有多少错误帧n_packetdata = 0;       % 发送了多少帧for ii = 1:nloop
%--------------------------发射端-------------------------------%% 生成一帧数据,串并转换,并QPSK,生成一帧frame_FDserial = rand(1,Nk*Nframe*Npsk) > 0.5;     % 发送的是bitframe_FDparallel = reshape(frame_FDserial,Nk,Nframe*Npsk);% 串并转换frame_mod = QPSKMod(frame_FDparallel,Nk,Nframe);     %调制% IFFTpower_FT = sum(sum(abs(frame_mod).^2))/Nk/Nframe;  % 计算下IFFT前的能量,FT表示频域frame_mod_shift = ifftshift(frame_mod);         % 频域归零frame_ifft = ifft(frame_mod_shift, Nfft);             % ifft% frame_ifft = ifft(frame_mod, Nfft);power_TD = sum(sum(abs(frame_ifft).^2))/Nk/Nframe; % 计算下IFFT前的能量,DT表示时域% 添加保护间隔frame_withGI = AddGI(frame_ifft, Nfft, NGI, Nframe, "CP");  % 添加保护间隔% 并串转换frame_TDserial = reshape(frame_withGI,1,Nsym*Nframe);% x=1:1:160;% hold on;% plot(x, frame_TDserial(1:160),'b');
%--------------------------Channel-------------------------------%% 信号先经历衰落channel = Rayleigh_model(Nchannel, PowerTDL);h = zeros(1, Tau_maxTDL);h(Delay+1) = channel;frame_conv = conv(frame_TDserial, h);frame_fading = frame_conv(:,1:length(frame_TDserial));        % 看似是线性卷积,实际上由于CP变成了循环卷积% 添加高斯白噪声power_TDserial = sum(abs(frame_TDserial).^2)/Nk/Nframe;     % 计算出的能量和理论不符啊,没发现问题在哪EsN0 = EbN0 + 10*log10(Npsk);                                  % 根据信噪比计算噪声能量,幅值,然后加在信号上N0 = power_TDserial .* 10.^(-EsN0/10);noise_msg = sqrt(N0 / 2) .* (randn(size(frame_TDserial)) + 1j * randn(size(frame_TDserial)));frame_recieved = frame_fading + noise_msg;% 陈老湿方法添加高斯白噪声,本质上是一样的
%       attn = sqrt(0.5*power_TDserial*SR/BR*10.^(-EbN0/10));
%       noise_msg = attn .* (randn(size(frame_TDserial)) + 1j * randn(size(frame_TDserial)));
%       frame_recieved = frame_TDserial + noise_msg;% plot(x, noise_msg(1:160),'r');% hold off;
%--------------------------接收端-------------------------------%% 接收端,串并转换frame_recieved_parallel = reshape(frame_recieved,Nsym,Nframe);% 去GIframe_noGI = RemoveGI(frame_recieved_parallel, Nfft, NGI);% FFTframe_recieved_FD_shift = fft(frame_noGI, Nfft);frame_recieved_FD = fftshift(frame_recieved_FD_shift);% frame_recieved_FD = fft(frame_noGI, Nfft);% 信道均衡H = fftshift(fft([h zeros(1, Nfft-Tau_maxTDL)].', Nfft));frame_equalization = frame_recieved_FD ./ repmat(H, 1, Nframe);% QPSK解调frame_demod = QPSKDemod(frame_equalization, Nk, Nframe);% 并串转换frame_output = reshape(frame_demod, 1, Nk*Nframe*Npsk);% 计算errorn_biterror_tmp = sum(abs(frame_output-frame_FDserial));n_bitdata_tmp = length(frame_FDserial);n_biterror = n_biterror + n_biterror_tmp;n_bitdata = n_bitdata + n_bitdata_tmp;if n_biterror_tmp ~= 0n_packeterror = n_packeterror + 1;endn_packetdata = n_packetdata + 1;end% 计算在当前信噪比下的误码率per = n_packeterror/n_packetdata;ber = n_biterror/n_bitdata;bers(kk)=ber;fprintf('%f\t%e\t%e\t%d\t\n',EbN0,ber,per,nloop);
end
save("BERofdm_rayleigh.mat",'bers');
%% 画图
% bers = load(!"BERofdm_rayleigh.mat").bers;
rayleigh_theory = 0.5.*(1-(1-(1./(10.^(EbN0s./10).*(48/80)+1))).^0.5);
semilogy(EbN0s,rayleigh_theory,'-*',EbN0s,bers,'-+');
xlabel('比特信噪比');
ylabel('误码率');
title('不同信噪比下误码率仿真曲线');
legend('理论曲线','实验曲线');

2. 代码分析

我们把信道卷积的代码拿出来看一下

PowerTDL_dB = [0 -8 -17 -21 -25];   % TDL中信道抽头的功率,dB为单位
Delay = [0 3 5 6 8];                % TDL中信道时延
PowerTDL = 10.^(PowerTDL_dB/10);    % TDL中信道抽头的功率
Nchannel=length(PowerTDL_dB);       % 信道抽头数
Tau_maxTDL = Delay(end)+1;          % 最大时延除以帧长,就是归一化的最大时延% 信号先经历衰落
channel = Rayleigh_model(Nchannel, PowerTDL);
h = zeros(1, Tau_maxTDL);
h(Delay+1) = channel;
frame_conv = conv(frame_TDserial, h);
frame_fading = frame_conv(:,1:length(frame_TDserial));        % 看似是线性卷积,实际上由于CP变成了循环卷积% 信道均衡
H = fftshift(fft([h zeros(1, Nfft-Tau_maxTDL)].', Nfft));
frame_equalization = frame_recieved_FD ./ repmat(H, 1, Nframe);

这里其实就涵盖了所有第二个仿真和第一个仿真的区别了,这个代码主要有这么几点是值得注意的

瑞利衰落信道是如何通过TDL模型仿真而成的

TDL模型里只包含两个内容,第一个是信道对功率的变化,这是用瑞利分布乘以功率来完成的。第二个是时延, 这是用信道的长度和线性卷积来完成的。我们看到代码中有一项Tau_maxTDL = Delay(end)+1; 这一般被称作信道长度。就是最大时延在TDL这个单位时延下的长度嘛。这样就能理解TDL是如何表达瑞利信道的了。

线性卷积,循环卷积和均衡

我们知道,在FFT中,循环卷积才是真正的卷积,各种性质都是在循环卷积中证明完成的,这其中当然包括大名鼎鼎的时域的卷积等于频域的点乘,也就是说,时域的循环卷积核频域的点乘结果是相同的。可是信道和信号在时域发生的实际上是线性卷积,这就给我们均衡的时候制造困难了,没有简便的算法啊!

我们在DSP课程中学过用循环卷积去计算线性卷积,方法就是把循环卷积的L扩展到M+N-1时候计算。那么有没有方法可以把线性卷积转化为循环卷积呢?当然有!这就是CP

具体原理可以看https://blog.51cto.com/u_15127585/2669966

这是非常巧妙的

也就是说CP有两个作用

  1. 在两个符号之间填充一点东西,这样可以消除掉ISI,当然这个作用用ZP就可以完成
  2. 实现循环卷积,大大的简化了信道的均衡
  3. 还有一个就是估计STO和CFO,这可以在前面的文章看到

线性卷积输入和输出长度怎么不相等了?

https://tieba.baidu.com/p/5402720283?red_tag=0784237449

这居然也被我查到了

这正是TDL想要的效果,我们多出来的实际上就是TDL产生的最大时延/TsT_sTs

三. 一些还没有思考清楚的问题

  1. 虚拟子载波放置在什么位置?他和ifftshift的关系是什么?
  2. 使用ZP时如何解决信道均衡的问题?如何将线性卷积转化为循环卷积?
  3. 误码率的误差来自哪里?

初识OFDM(六):从零开始的OFDM误码率仿真相关推荐

  1. 初识MIMO(六):MU-MIMO的仿真

    初识MIMO(六):MU-MIMO的仿真 零 代码地址 https://github.com/liu-zongxi/MIMO_simulation 请大家看完觉得有用别忘了点赞收藏,github项目给 ...

  2. 基于ZF均衡算法的OFDM调制解调系统的MATLAB仿真

    UP目录 一.理论基础 二.核心程序 三.测试结果 一.理论基础 OFDM(Orthogonal Frequency Division Multiplexing)即正交频分复用技术,实际上OFDM是M ...

  3. 初识MIMO(五):CSI反馈及其仿真

    初识MIMO(五):CSI反馈及其仿真 零 代码地址 https://github.com/liu-zongxi/MIMO_simulation 一. 发射端的信道估计 这章其实给了我隔靴搔痒的感觉, ...

  4. 从零开始学ros小车仿真

    从零开始学ros小车仿真 从零开始学ros小车仿真 目录 1.从零开始学ros小车仿真(一)在solidworks中建模小车并转出为urdf文件 2.从零开始学ros小车仿真(二)在rviz中检验导入 ...

  5. 2ASK和2FSK相干解调误码率仿真matlab

    本文结构 2ASK误码率仿真 2FSK误码率仿真 2ASK误码率仿真 2ASK相干解调法: 图片来源:郭心悦 发送端发送信号经过信道:经过信道后的波形通过带通滤波器,带通滤波器会有输出波形y(t)y( ...

  6. ROS探索总结(四)(五)(六)——简单的机器人仿真 创建简单的机器人模型smartcar 使用smartcar进行仿真

    ROS探索总结(四)--简单的机器人仿真 前边我们已经介绍了ROS的基本情况,以及新手入门ROS的初级教程,现在就要真正的使用ROS进入机器人世界了.接下来我们涉及到的很多例程都是<ROS by ...

  7. 分别对比SVD和GMD非码本的预编码以及DFT和TxAA码本预编码的MIMO系统matlab误码率仿真

    up目录 一.理论基础 二.核心程序 三.测试结果 一.理论基础 对比SVD和GMD非码本的预编码以及DFT和TxAA码本预编码的MIMO系统matlab误码率仿真 二.核心程序 function V ...

  8. 4PAM的误码率仿真

    1.问题描述: 4PAM的误码率仿真 2.部分程序: %BER of 4PAM clear all;clc; Nb=10000; %the number of data bits EbN0dB=1:3 ...

  9. 六自由度方程组 matlab,采用Matlab的六自由度机器人三维运动学仿真_李庆.pdf

    采用Matlab的六自由度机器人三维运动学仿真_李庆 第 ( ) 3 卷 第 期 华侨大学学报 自然科学版 7 3 Vol.37 No.3 年 月 ( ) 2016 5 JournalofHua ia ...

最新文章

  1. 关于WordPress中字体加载慢的问题解决方案(转)
  2. R语言数据转换(split-apply-combin…
  3. 正斜杠( / )和反斜杠( \ )的区别
  4. Linux内核调试的方式以及工具集锦
  5. linux创建空镜像,Docker创建base镜像
  6. 在Eclipse或工作空间中 ,复制或修改项目后,把项目部署后发现还是原来的项目名称...
  7. bash中的特殊文件夹
  8. 《Nature-Inspired Metaheuristic Algorithms》——蝙蝠算法 Bat Algorithm
  9. 【树莓派】搭建局域网内网页实时视频监控
  10. vscode node 乱码 非中文乱码
  11. 18道kafka高频面试题哪些你还不会?(含答案和思维导图)
  12. 希捷7200.12 固件问题重新上演
  13. 百度 bos php,百度BOS云存储上传插件 - 支持客户端直传、服务端中转、分片上传 – 基于ThinkPHP和Bootstrap的极速后台开发框架...
  14. [转载]物理大神的八卦完整版——大爱物理
  15. 2021年6月四六级成绩查询时间!
  16. 使用 Shiro 配合微信小程序或者app登录,做验权
  17. 有个美剧叫美国恐怖故事_5个系统管理员恐怖故事
  18. AUTOEXEC.BAT及CONFIG.SYS文件用法
  19. Word中的拼页、书籍折页、反向书籍折页(二)
  20. QListWidget removeItemWidget注意事项

热门文章

  1. 熊海CMS 1.0代码审计漏洞集合
  2. R语言时代,你要怎样画地图?
  3. linux组的管理:修改组名字,删除组,使用sudo权限
  4. 学习软件的一点心得体会
  5. 10 EPC与物联网
  6. linux中bash是什么命令,Bash简介 Bash是如何处理命令的
  7. 为什么电影里黑客几乎不用鼠标?
  8. 【GStreamer】基于NTP+SEI的视频流传输时延测量
  9. 股市----别人的经验
  10. 大数据时代比较教育研究范式的转型