由于是第一篇博客,想先说点废话,其实自己早就想把学到的一些东西总结成文章随笔之类的供自己复习时查看的了。但是一是觉得自己学的的不够深入,总结也写不出什么很深刻的东西;二是觉得网上也有海量的资料了,需要时查一查根本不需要自己写。但是恰恰也是网上的资料过于庞大,良莠不齐,导致每次都如海水一样的知识涌入脑中,最后也如走马观花一般了了看下,知识吸收率低的惊人。现在也准备改变一下观念,尽量把自己学过的东西归纳整理,以随笔的形式发出来,可能有些地方我还不能理解作者的做法,我也会记录出来,懂的地方解释清楚,不懂的地方也标记清楚,同时在之后的不断学习和总结中补上之前挖的坑,强迫症写文章真的是太难了~哭。
不过也这样安慰自己吧,将所学的进行输出整理其实也是很重要的一步,看似时间浪费了好多,不过读书人花的时间怎么能叫浪费呢!整理出来自己看着不爽吗。如果能同时帮助到其他人的话,那就太好了!希望自己能坚持下去。
关于OFDM系统我目前参考的是《MIMO-OFDM无线通信技术及MATLAB实现》这本书,我看到网上用这本书做参考的人还挺多的,这里将将作者实现OFDM系统的思路及其代码重新理顺一遍。因为我理解的也不是很深入,有不对的地方希望大佬能帮忙指正。如果是没怎么接触过OFDM的萌新,这篇文章可以帮助你对OFDM地仿真有个粗浅的了解。
首先画一个我个人认为特别好理解的OFDM符号变化图来帮助理解代码,接下来我会详细的介绍每个步骤在干什么。
步骤0>
先假装前景摘要一下。本OFDM系统仿真用到的技术主要有:16QAM调制解调 IFFT与FFT 添加AWGN噪声
,没用到的有:信道编码 交织 多径瑞利信道 信道估计
等等,哇,没用到的技术还有这么多,惭愧惭愧。这些技术的数学理论推导确实很难,但是在MATLAB仿真中往往用几个自带的函数就能解决问题,所以要实现一个简单的OFDM系统还是很容易的,不要被天花乱坠般恐怖的数学公式吓跑了(所以我最喜欢的就是直接看代码的运行过程,然后有时间再去研究数学推导2333)。
步骤1>
这个仿真好像暂时没有时间的概念,单位是按照采样点来的。假设一帧有三个OFDM符号,每个符号长度为64(刚好在步骤3做IFFT时长度也为64,满足2的幂次方)。我们首先生成数字基带信号,信号长度为192个采样点,由于要进行16QAM调制,我们直接随机生成192个16进制的数作为基带信号X(K),然后再将X(K)经过16QAM星座图映射便完成了调制。
另外在步骤1我们还要进行信噪比的一些初始化,便于计算噪声幅度和最后的计算比特误码率。
代码:
clc; clear all; clode all
NFRAME = 3; % 每一帧的OFDM符号数
NFFT = 64; % 每帧FFT长度
NCP = 16; % 循环前缀长度
NSYM = NFFT + NCP; % OFDM符号长度
M = 16; K = 4; % M:调制阶数,K:log2(M)
EbN0 = 0; % 设出比特信噪比(dB)
snr = EbN0 + 10 * log10(K); % 由公式推出snr(dB)表达式,公式见信噪比补充部分
BER(1 : length(EbN0)) = 0; % 初始化误码率
X = randi([0,15],1,NFFT * NFRAME); % 生成数字基带信号
X_mod = qammod(X,M,'gray') / (sqrt(10)); % 16QAM调制,格雷码星座图,并归一化
星座图补充:
简单来说,16QAM星座图映射就是将一个16进制的数根据一定的规律变成一个复数。具体的数学推导我们这暂时不多加讨论,只写实现方法。比如下图是一个按格雷码编码的16QAM星座图(为什么是格雷码呢,你看它相邻码的二进制都只有1位不同)。看星座图可知,假设数字信号X为5,经过qammod()函数后X_mod变为-a+bi
;同理假设数字信号X为13,经过qammod()函数后X_mod变为a+bi
,其中a和b都是一个确定的数。注意最后要进行归一化处理除以科研小生,怎么能不进行误码率分析呢?于是当前步骤我们研究一下怎么分析误码率。其实也很简单,计算一下Yr和X有几比特不相同,再计算一下总共有几比特,把它们相除就得到了我们的比特误码率(BER)。
需要注意的一点是,既然是误比特率,就要把16进制的信号转换成2进制,以比特为单位计算错误数
代码:
Neb = sum(sum(de2bi(Yr,K) ~= de2bi(X,K))); % 转为2进制,计算具体有几bit错误
Ntb = NFFT * NFRAME * K; % 仿真的总比特数
BER = Neb / Ntb;
完整代码:
最后贴一个完整代码,代码是参考的《MIMO-OFDM无线通信技术及MATLAB实现》这本书。我是一行一行自己重新实现了一遍并且加上了详细的中文注释,希望能对像我这样的刚入门的萌新有所启发。对了,后面有个与理论值相比较的作图函数有点占位置,我就暂时不放到这篇文章中了XD。
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% Version 3.0
%%% 16QAM调制(官方函数)
%%% IFFT(官方函数)
%%% 添加循环前缀(单径AWGN中CP好像没啥用??)
%%% AWGN信道
%%% 去除循环前缀
%%% FFT(官方函数)
%%% 16QAM解调(官方函数)
%%% BER分析
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
clear all;close all;clc;
%% 基带数字信号及一些初始化
NFRAME = 3; % 每一帧的OFDM符号数
NFFT = 64; % 每帧FFT长度
NCP = 16; % 循环前缀长度
NSYM = NFFT + NCP; % OFDM符号长度
M = 16; K = 4; % M:调制阶数,K:log2(M)
EbN0 = 0:1:20; % 设出比特信噪比(dB)
snr = EbN0 + 10 * log10(K); % 由他俩关系推出(dB)
BER(1 : length(EbN0)) = 0; % 初始化误码率
file_name=['OFDM_BER_NCP' num2str(NCP) '.dat'];
fid=fopen(file_name, 'w+');
X = randi([0,15],1,NFFT * NFRAME); % 生成数字基带信号
%%
for i = 1 : length(EbN0) % 对于每一种比特信噪比,计算该通信环境下的误码率
%% 16QAM调制(官方函数)
X_mod = qammod(X,M,'gray') / (sqrt(10)); % 16QAM调制,格雷码星座图,并归一化
%% IFFT与循环前缀添加
x(1 : NFFT * NFRAME) = 0; % 预分配x数组
xt(1 : (NFFT + NCP) * NFRAME) = 0; % 预分配x_t数组
len_a = 1 : NFFT; % 处理的X位置
len_b = 1 : (NFFT + NCP); % 处理的X位置(加上CP的长度)
len_c = 1 : NCP;
len_left = 1 : (NFFT / 2); len_right = (NFFT / 2 + 1) : NFFT; % 每一符号分为左右两边??
for frame = 1 : NFRAME % 对于每个OFDM符号都要翻转和IFFT
x(len_a) = ifft([X_mod(len_right), X_mod(len_left)]); % 左右翻转再ifft
xt(len_b) = [x(len_c + NFFT - NCP), x(len_a)]; % 添加CP后的信号数组
len_a = len_a + NFFT; % 更新找到下一个符号起点位置
len_b = len_b + NFFT + NCP; % 更新找到下一个符号起点位置(CP开头)
len_c = len_c + NFFT;
len_left = len_left + NFFT; len_right = len_right + NFFT;
end
%% 由snr计算噪声幅度并加噪
% P_s = xt * xt' / NSYM / NFRAME; % 计算信号功率
% delta1 = sqrt(10 .^ (-snr(i) / 10) * P_s / 2); % 计算噪声标准差
% yr = delta1 * (randn(size(xt)) + 1i * randn(size(xt)));
yr = awgn(xt,snr(i),'measured'); % 官方函数直接加噪
%% 去除循环前缀并且FFT
y(1 : NFFT * NFRAME) = 0; % 预分配y数组
Y(1 : NFFT * NFRAME) = 0; % 预分配Y数组
len_a = 1 : NFFT; % 处理的y位置
len_b = 1 : NFFT; % 处理的y位置
len_left = 1 : (NFFT / 2); len_right = (NFFT / 2 + 1) : NFFT; % 每一符号分为左右两边
for frame = 1 : NFRAME % 对于每个OFDM符号先去CP,再FFT再翻转
y(len_a) = yr(len_b + NCP); % 去掉CP
Y(len_a) = fft(y(len_a)); % 先fft再翻转
Y(len_a) = [Y(len_right), Y(len_left)];
len_a = len_a + NFFT;
len_b = len_b + NFFT + NCP;
len_left = len_left + NFFT; len_right = len_right + NFFT;
end
%% 16QAM解调(官方函数)
Yr = qamdemod(Y * sqrt(10),M,'gray');
%% BER计算
Neb = sum(sum(de2bi(Yr,K) ~= de2bi(X,K))); % 转为2进制,计算具体有几bit错误
Ntb = NFFT * NFRAME * K; %仿真的总比特数
BER(i) = Neb / Ntb;
fprintf('EbN0 = %d[dB], BER = %d / %d = %.3f\n', EbN0(i),Neb,Ntb,BER(i))
fprintf(fid, '%d %5.3f\n', EbN0(i),BER(i));
end
%% BER作图分析
fclose(fid);
disp('Simulation is finished');
plot_ber(file_name,K);
几个疑问:
有几个没搞懂的地方还是记录一下
- 这个程序中添加的循环前缀好像并没有什么作用,不知道是不是AWGN信道的原因,在多径瑞利衰落信道中倒是与信道冲激响应有个卷积操作,这里好像添加了CP也没用到就去除了。
- 进行IFFT前为什么要把每个OFDM符号进行左右交换,不是很懂欸。
参考文献:
[1] 张少侃,吕聪敏,甘浩.数字通信系统中Eb/N0与SNR转换方法的研究[J].现代计算机,2019(12):33-36.
[2] Cho Y S, Kim J, Yang W Y, et al. MIMO-OFDM wireless communications with MATLAB[M]. John Wiley & Sons, 2010.