通信系统仿真速成第6天:OFDM基带仿真(简单教学版)

OFDM(Orthogonal Frequency Division Multiplexing)即正交频分复用技术,实际上OFDM是MCM(Multi Carrier Modulation),多载波调制的一种。
OFDM技术由MCM(Multi-Carrier Modulation,多载波调制)发展而来。OFDM技术是多载波传输方案的实现方式之一,它的调制和解调是分别基于IFFT和FFT来实现的,是实现复杂度最低、应用最广的一种多载波传输方案。
在通信系统中,信道所能提供的带宽通常比传送一路信号所需的带宽要宽得多。如果一个信道只传送一路信号是非常浪费的,为了能够充分利用信道的带宽,就可以采用频分复用的方法。
OFDM主要思想是:将信道分成若干正交子信道,将高速数据信号转换成并行的低速子数据流,调制到在每个子信道上进行传输。正交信号可以通过在接收端采用相关技术来分开,这样可以减少子信道之间的相互干扰(ISI) 。每个子信道上的信号带宽小于信道的相关带宽,因此每个子信道上可以看成平坦性衰落,从而可以消除码间串扰,而且由于每个子信道的带宽仅仅是原信道带宽的一小部分,信道均衡变得相对容易。

OFDM技术是HPA联盟(HomePlug Powerline Alliance)工业规范的基础,它采用一种不连续的多音调技术,将被称为载波的不同频率中的大量信号合并成单一的信号,从而完成信号传送。由于这种技术具有在杂波干扰下传送信号的能力,因此常常会被利用在容易受外界干扰或者抵抗外界干扰能力较差的传输介质中。

百度百科:OFDM

参考书籍:MIMO-OFDM Wireless Communications with MATLAB

通信系统仿真速成第6天:OFDM基带仿真(简单教学版)_第1张图片

书上代码都有了……

2015年带几个本科生做的课程设计就是这个。


任务(2015年10月10日10:57:49)

 

我猜其实你们是不愿意做什么课程设计的,所以我特意选了个简单的。(见下面红字)

 

参考书籍:《MIMO-OFDM Wireless Communications with MATLAB》第4-6章

第4章OFDM简介

第5章 OFDM同步

第6章 OFDM信道估计

 

自学4-6章。

 

OFDM系统框图如下。

通信系统仿真速成第6天:OFDM基带仿真(简单教学版)_第2张图片


 

对于Matlab编程的说明。

(1)最低要求。第4章,明白为什么要用OFDM。编出一个最简单的OFDM系统,画出发射、接收的星座图。计算误码率。

调制格式为QPSK。信源发出数据,星座图映射,S/P,IFFT,加CP。传输。加信道(AWGN或瑞利)。加噪声(AWGN)。接收。恢复。

(2)稍高层次的要求。自己设置定时误差、频率偏移。并恢复。

(3)更高层次的要求。加信道均衡。

 

各个流程的代码几!乎!都!有!了!

大家都争取自己写一写,每人写一点点,拼一个较完整的OFDM的Matlab仿真程序。

最后做PPT的话,看你们代码完成程度。


仿真Eb/N0从x dB到y dB时,QPSK-OFDM系统的误比特率性能。

伪代码如下。

开始。

0. 对于一个给定的信噪比Eb/N0

1. 产生二进制序列(信源)

2. QPSK星座点格雷映射

3. 串行数据转并行数据

4. IFFT

5. 加入循环前缀CP

6. 并行数据转串行数据,形成OFDM信号

7. 计算OFDM信号的功率

8. 根据0.中的信噪比和由7.得出的OFDM信号功率,计算噪声功率

9. 产生AWGN,均值为0,方差为8.中计算得到的噪声功率

10. 向6.中的OFDM信号加入AWGN

11. 去掉5.加入的循环前缀

12. 串行数据转并行数据

13. FFT,即4.的逆变换

14. 并行数据转串行数据

15. 根据QPSK星座点格雷映射,由接收到的符号判决输出二进制序列,即2.的逆操作

16. 与1.中的原始二进制序列对比,统计错误的比特数,计算误码率

17. 改变0.中的Eb/N0,重复1-16

结束。


仿真Eb/N0从x dB到y dB时,QPSK-OFDM系统的误比特率性能。

伪代码如下。

开始。

0. 对于一个给定的信噪比Eb/N0

1. 产生二进制序列(信源)

2. QPSK星座点格雷映射

3. 串行数据转并行数据

4. IFFT

5. 加入循环前缀CP

6. 并行数据转串行数据,形成OFDM信号

7. 计算OFDM信号的功率

8. 根据0.中的信噪比和由7.得出的OFDM信号功率,计算噪声功率

9. 产生AWGN,均值为0,方差为8.中计算得到的噪声功率

10. 向6.中的OFDM信号加入AWGN

11. 去掉5.加入的循环前缀

12. 串行数据转并行数据

13. FFT,即4.的逆变换

14. 并行数据转串行数据

15. 根据QPSK星座点格雷映射,由接收到的符号判决输出二进制序列,即2.的逆操作

16. 与1.中的原始二进制序列对比,统计错误的比特数,计算误码率

17. 改变0.中的Eb/N0,重复1-16

结束。


通信系统仿真速成第6天:OFDM基带仿真(简单教学版)_第3张图片

通信系统仿真速成第6天:OFDM基带仿真(简单教学版)_第4张图片

通信系统仿真速成第6天:OFDM基带仿真(简单教学版)_第5张图片


注。有人始终纠结,这到底是频域符号还是时域信号。

这个无所谓啊!傅里叶变换前后本来就是同一个东西-_-!!

有人非要说,ifft之前是频域的。我也是⊙﹏⊙b汗。

那我问你,如果x是时域,X = fft(x),那X是频域了吧。那继续fft呢?Y = fft(X)。呵呵…

啥啥啥时域对偶,等距同构……数学学好了再说话。数学这么烂,搞什么自然科学?


尽管网上代码一抓就是一大把,我还是再贴一个版本上来。


Main函数

%% 基本QPSK-OFDM

% 不用VC,加CP。
% QPSK使用gray码
% 书上p.115
% AWGN信道,循环前缀,QPSK
% 开始时间:2015年10月13日12:44:48
% 版本编号:v1.1

% 整体思路:
% 1、算信号功率 (自己循环N_iter次)
% 2、根据1的结果算噪声幅度
% 3、再产生信号+噪声
% 4、判决,计算错误比特数
% (3和4循环N_iter次)

clear;
close all;
clc

EbN0_dB = 0:1:25;
N_iter = 1e3; % 重复N_iter次试验 1e3基本就可以了。
% 1e5相当准确了。但是等待的时间很漫长。matlab解释执行,慢。

Nbps = 2;
N_LEVEL = 2^Nbps; % QPSK 调制阶数
Nfft = 64;
Nvc = 0;
Nused = Nfft - Nvc; % 不用虚拟载波
Ng = Nfft/4;
Nsym = Nfft + Ng;
Nframe = 3; %每一帧的符号数?? 还是仿真的帧数??
symbol_total = Nfft * Nframe; % 一次仿真信源输出的所有码元总数
x_GI = complex(zeros(Nsym * Nframe,1),0);
Y = complex(zeros(symbol_total,1),0);
% gray_r =  zeros(symbol_total,1); % 收到的格雷码
symbol_output = zeros(symbol_total,1);
bers = zeros(size(EbN0_dB));
%% 1、计算信号功率(只需计算一次)
% 思路:用很长一段信号的能量 除以 采样点的个数 可以得 时间平均的功率
sigEng = 0; %信号能量
for m = 1:N_iter
    symbol = randi(N_LEVEL,symbol_total,1) - 1; % 信源发出的码元总数,不是二进制的个数
    
    graycode = symbol;
    % 将码元编为gray码 (对于QPSK,就是把3和2换一下位置)
    index_3 = find(symbol==3);
    index_2 = find(symbol==2);
    graycode(index_3) = 2;
    graycode(index_2) = 3;
    
    data_QPSK = exp(1j * graycode* pi/2 + 1j * pi/4); % 映射为QPSK信号星座点 scatterplot(data_QPSK);
        
    for k = 1:Nframe %处理每一帧的数据 S/P Nframe个串行的转成1帧
        kk1 = (k-1) * Nfft + (1:Nfft);
        kk2 = (k-1) * Nsym + (1:Nsym);
        X = data_QPSK(kk1);
        x = ifft(X);
        x_GI(kk2) = guard_interval(Ng,Nfft,x);
    end
    
    sigEng = sigEng + x_GI' * x_GI; %共轭转置
end

% 信号功率
sigPow = sigEng/Nsym/Nframe/N_iter;

%% 2、计算噪声幅度
snrs = EbN0_dB + 10 * log10(Nbps*(Nused/Nfft)); % 时域信噪比 SNRt
noise_mags = sqrt((10.^(-snrs/10))*sigPow/2); % dB换算为线性刻度

%% 3、再产生信号,并加入噪声
for k_EbN0 = 1:length(EbN0_dB)
    
    ber_count = 0; % 初始化 误bit数
    
    for m = 1:N_iter
        
        % Info Source & Mod
        symbol = randi(N_LEVEL,symbol_total,1) - 1; % 信源
        graycode = symbol;
        % 将码元编为gray码 (对于QPSK,就是把3和2换一下位置)
        index_3 = find(symbol==3);
        index_2 = find(symbol==2);
        graycode(index_3) = 2;
        graycode(index_2) = 3;
        
        data_QPSK = exp(1j * graycode* pi/2 + 1j * pi/4); % 映射为QPSK信号星座点 scatterplot(data_QPSK);
%          scatterplot(data_QPSK);  【注意】千万不要在整个for循环里面画图!卡爆!要单步运行,看一看即可。
%          title('QPSK X 频域信号');
%          grid on;
         
        % Tx
        for k = 1:Nframe %处理每一帧的数据 S/P Nframe个串行的转成1帧
            kk1 = (k-1) * Nfft + (1:Nfft);
            kk2 = (k-1) * Nsym + (1:Nsym);
            X = data_QPSK(kk1); % 每次循环,X的值在变
            x = ifft(X);
            x_GI(kk2) = guard_interval(Ng,Nfft,x);
        end
        
        % Channel
        y = x_GI;
        
        % AWGN 【注意】实部和虚部是独立的!所以要randn两次
        noise_mag = noise_mags(k_EbN0);
        complex_noise = noise_mag * (randn(size(y))+1j*randn(size(y))); % y(t) + noise(t)
        y_GI = y + complex_noise;
        
        % Rx
        for k = 1:Nframe % 这应该是Nframe帧吧!就是一共发了这么多帧。并行以后还有Nframe个
            kk2 = (k-1) * Nsym + (1:Nsym);
            kk1 = (k-1) * Nfft + (1:Nfft);
            
            Y(kk1) = fft(remove_GI(Ng,Nsym,y_GI(kk2)));
        end
        
%         % 在这里可以看收到信号的星座图 %scatterplot(Y);
%         【注意】千万不要在整个for循环里面画图!会卡爆的!要单步运行,看一看即可。
%         if m == 1
%             scatterplot(Y);
%             grid on;
%             plot_title = ['E_b/N_0 = ' num2str(EbN0_dB(k_EbN0)) ' dB'];
%             title(plot_title);
%         end
        
        % DeMod
        % 解调(其实就是去映射,因为没有放到高频上去) 判决
        y_real = real(Y); % 向量操作!节省时间!
        y_imag = imag(Y); % 向量操作!

%         scatterplot(Y);

        % 判决
        % 判决:直接将格雷码判决为自然数 
        for n = 1:symbol_total
            r_value = y_real(n);
            i_value = y_imag(n);
            if r_value > 0 && i_value >0
                symbol_output(n) = 0;
            elseif r_value < 0 && i_value < 0
                symbol_output(n) = 3;
            elseif r_value < 0 && i_value > 0
                symbol_output(n) = 1;
            else
                symbol_output(n) = 2;
            end
        end
        %         ber_count = ber_count + sum(nnz(data_output~=symbol));
        
        % 计算BER
        % 1.找出 收到的格雷码 和 原始的 格雷码 不同的index
        % 2.对于每一个不同的 格雷码符号,看错成了哪个,再累计错了几位
        error_index = find(symbol_output~=symbol);
        
        for error_k = 1:length(error_index)
            k_temp = error_index(error_k);
            error_v = symbol_output(k_temp);
            origin_v = symbol(k_temp);
            
            % 0 --> 1 或 0 --> 2 错了1位
            % 0 --> 3 错了2位
            if origin_v == 0
                if error_v == 1 || error_v == 2
                    ber_count = ber_count + 1;
                else
                    ber_count = ber_count + 2;
                end
                
                % 1 --> 0 或 1 --> 3 错了1位
                % 1 --> 2 错了2位
            elseif origin_v == 1
                if error_v == 0 || error_v == 3
                    ber_count = ber_count + 1;
                else
                    ber_count = ber_count + 2;
                end
                
                
                % 2 --> 0 或 2 --> 3 错了1位
                % 2 --> 1 错了2位
            elseif origin_v == 2
                if error_v == 0 || error_v == 3
                    ber_count = ber_count + 1;
                else
                    ber_count = ber_count + 2;
                end
                
                % 3 --> 1 或 3 --> 2 错了1位
                % 3 --> 0 错了2位
            else
                if error_v == 1 || error_v == 2
                    ber_count = ber_count + 1;
                else
                    ber_count = ber_count + 2;
                end
            end
            
        end
        
    end
    
    % 总的比特数是多少?!!!! QPSK!!! 每个符号2个比特!!
    ber = ber_count/symbol_total/N_iter/2; %ber (平均值)!!
    bers(k_EbN0) = ber;
end

%% 4、绘图

% bers
% 理论分析的 AWGN信道 QPSK的BER线
M = N_LEVEL;
Pe_AWGN = ber_QAM(0:EbN0_dB(end),M,'AWGN');
semilogy(0:EbN0_dB(end),Pe_AWGN,'r-');hold on;grid on;
semilogy(EbN0_dB,bers,'b*'),
legend('AWGN analytic', 'Simulation');
xlabel('E_b/N_0 [dB]'), ylabel('BER'); axis([0 10 1e-6 1])

guard_interval

function x_GI = guard_interval( Ng,N_FFT,x )
% 作用:加入CP
% 说明:输入N_FFT个点,输出N_SYM个点
% 版本号:v1.0
% 开始时间:2015年10月12日21:55:21

x_GI = [ x(N_FFT-Ng+1:N_FFT); x];

end

remove_GI

function y=remove_GI(Ng,Nsym,ofdmSym)

%MIMO-OFDM Wireless Communications with MATLAB㈢   Yong Soo Cho, Jaekwon Kim, Won Young Yang and Chung G. Kang
%?2010 John Wiley & Sons (Asia) Pte Ltd

    y=ofdmSym(Ng+1:Nsym);
end

Q

function y=Q(x)
% co-error function: 1/sqrt(2*pi) * int_x^inf exp(-t^2/2) dt.
y=erfc(x/sqrt(2))/2;

ber_QAM
function ber=ber_QAM(EbN0dB,M,AWGN_or_Rayleigh)
% Find ananlytical BER of Mary QAM in AWGN or Rayleigh channel
% EbN0dB=EbN0dB: Energy per bit-to-noise power[dB] for AWGN channel
%       =rdB: Average SNR(2*sigma Eb/N0)[dB] for Rayleigh channel
% M = Modulation order (Constellation size)  

%MIMO-OFDM Wireless Communications with MATLAB㈢   Yong Soo Cho, Jaekwon Kim, Won Young Yang and Chung G. Kang
%?2010 John Wiley & Sons (Asia) Pte Ltd

N= length(EbN0dB);  sqM= sqrt(M); 
a= 2*(1-power(sqM,-1))/log2(sqM);  b= 6*log2(sqM)/(M-1);
if nargin<3,  AWGN_or_Rayleigh='AWGN';  end
if lower(AWGN_or_Rayleigh(1))=='a', ber = a*Q(sqrt(b*10.^(EbN0dB/10)));
 else   rn= b*10.^(EbN0dB/10)/2; ber = 0.5*a*(1-sqrt(rn./(rn+1)));
end


你可能感兴趣的:(通信系统)