当前位置: 首页 > news >正文

基于MATLAB实现的OFDM仿真调制解调,BPSK、QPSK、4QAM、16QAM、32QAM,加性高斯白噪声信道、TDL瑞利衰落信道

基于MATLAB实现的OFDM仿真调制解调,BPSK、QPSK、4QAM、16QAM、32QAM,加性高斯白噪声信道、TDL瑞利衰落信道

相关链接

OFDM中的帧(frame)、符号(symbol)、子载波(subcarriers)、导频(Pilot)、保护间隔(guard)的关系图解以及代码详解–MATLAB

【学习笔记】OFDM的原理和技术介绍以及仿真结果分析附代码–MATLAB

1 加性高斯白噪声信道

在本程序中,通过MATLAB仿真了OFDM的发射、信道、接收解调的过程。支持的BPSK、QPSK、多种QAM的解调方式,并计算了不同信噪比下的误比特率。高斯白噪声信道下,不需要信道估计和信道均衡,但是在衰落信道中必须要信道估计或信道均衡,或者两个都使用,才能正确的解调。以下在衰落信道中的解调只实现了信道均衡。

1.1 BPSK和QSPK

%-----------------------仿真OFDM---------------------------%
%% 设置参数
clear;clc;
Nk = 128; % 子载波个数
Nfft = 128; % fft长度
Nframe = 6; % 一帧中有几个OFDM符号M = 2; % 调制符号所含比特(改为1:BPSK,2:QPSK)
SR = 250000; % 符号速率
BR = SR .* M; % 比特率
NGI = 32; % 保护间隔长度
EbN0s = 0:1:12; % 信噪比
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 = randi([0 1], 1, Nk * Nframe * M);% 发送的是bitframe_FDparallel = reshape(frame_FDserial, Nk, Nframe * M); % 串并转换if M==1frame_mod = BPSKMod(frame_FDparallel, Nk, Nframe); % BPSK调制elseif M==2frame_mod = QPSKMod(frame_FDparallel, Nk, Nframe); % QPSK调制end% IFFTpower_FT = 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;% 加性白高斯噪声信道         power_TDserial = sum(abs(frame_TDserial).^2) / length(frame_TDserial); % 计算发送序列的能量EsN0 = EbN0 + 10 * log10(M); % 根据信噪比计算Es/N0(dB)噪声能量,幅值,然后加在信号上N0 = power_TDserial / (10^(EsN0 / 10));noise_msg = sqrt(N0 / 2) .* (randn(size(frame_TDserial)) + 1i * randn(size(frame_TDserial)));frame_recieved = frame_TDserial + noise_msg;% 接收端,串并转换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);if M==1% BPSK解调frame_demod = BPSKDemod(frame_recieved_FD, Nk, Nframe);elseif M==2% QPSK解调frame_demod = QPSKDemod(frame_recieved_FD, Nk, Nframe);end% 并串转换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);
endsemilogy(EbN0s, bers, '-+');
xlabel('比特信噪比');
ylabel('误码率');
title('不同信噪比下误码率仿真曲线');
if M==1legend('BPSK实验曲线');
elselegend('QPSK实验曲线');
endfunction outs = QPSKMod(input_data,nk,nframe,m)
% 输入
% input_data: 待数字调制的输入数据(Nk,Nframe*M)
% nk: 子载波个数,也就是并联个数
% nframe: 一帧中包含多少OFDM符号
% m: 调制数
% 输出
% outs: (nk,nframe),输出a+bi
% out_coss:(nk,nframe),输出实部
% out_sins:(nk,nframe),输出虚部if nargin < 4                   % 设置默认值m = 2;
endouts = zeros(nk,nframe);    % 初始化
out_cos = zeros(nk,nframe);
out_sin = zeros(nk,nframe);input_data_pn = 2*input_data - 1;     % 把0,1映射到-1,1
A = 1/sqrt(2);                        % 归一化幅值out_cos((1:nk),(1:nframe)) = input_data_pn((1:nk), (1:2:2*nframe-1)) .* A;    % 每次使用两列
out_sin((1:nk),(1:nframe)) = input_data_pn((1:nk), ((2:2:2*nframe))) .* A;outs = out_cos + out_sin * 1j;
endfunction outputs = QPSKDemod(input_data,nk,nframe)
% 输入
% input_data: (Nk, Nframe), 一个频域的复数,会被拆开解调
% nk: 频域并联
% nframe: 一帧包含符号数
% 输出
% outputs:(Nk, 2*Nframe), 解调后,多出一倍,全是01
outputs = zeros(nk,2*nframe);
A = 1/sqrt(2); 
input_data = input_data ./ A;
outputs((1:nk),(1:2:2*nframe-1)) = real(input_data((1:nk),(1:nframe)))>=0;
outputs((1:nk),(2:2:2*nframe)) = imag(input_data((1:nk),(1:nframe)))>=0;endfunction outs = BPSKMod(input_data,nk,nframe,m)
% BPSK调制函数
% 输入
% input_data: 待数字调制的输入数据(Nk,Nframe*M)
% nk: 子载波个数,也就是并联个数
% nframe: 一帧中包含多少OFDM符号
% m: 调制数,默认值为1
% 输出
% outs: (nk,nframe),输出复数
A = 1/sqrt(2);    % 归一化幅值
outs = input_data * 2 - 1;  % 将二进制0/1映射为复数-1/1
outs = A * outs;  % 乘以归一化幅值
endfunction outputs = BPSKDemod(input_data,nk,nframe)
% BPSK解调函数
% 输入
% input_data: (Nk, Nframe), 一个频域的复数
% nk: 频域并联
% nframe: 一帧包含符号数
% 输出
% outputs:(Nk, Nframe), 解调后的二进制数据
outputs = real(input_data) >= 0;  % 判断实部是否大于等于0,大于等于0表示1,小于0表示0
endfunction output_TD = AddGI(input_TD, nfft, nGI, nframe, type_GI)
if type_GI=="CP"    % 实现CPoutput_TD = [input_TD(nfft-nGI+1:nfft, :); input_TD(1:nfft, :)];
elseif type_GI=="ZP" % 实现ZPoutput_TD = [zeros(nGI,nframe); input_TD(1:nfft, :)];
end
end
function output_TD = RemoveGI(input_TD,nfft,nGI)
% 输入
% input_TD: (Nsym,Nframe)输入的并联时域数据
% nfft:fft长度
% nGI: GI长度
% 输出
% output_TD: (Nfft,Nframe)去掉GI后的并联时域数据output_TD = input_TD(nGI+1:nfft+nGI,:);
end

在这里插入图片描述

1.2 QAM

M = 16; % 调制符号所含比特(改为4:4QAM,16:16QAM,32:32QAM)

%% 设置参数
clear;clc;
Nk = 128; % 子载波个数
Nfft = 128; % fft长度
Nframe = 6; % 一帧中有几个OFDM符号
M = 16; % 调制符号所含比特(改为4:4QAM,16:16QAM,32:32QAM)
SR = 250000; % 符号速率
BR = SR * log2(M); % 比特率(根据调制方式计算比特率)
NGI = 32; % 保护间隔长度
EbN0s = 0:1:12; % 信噪比
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 = randi([0 1], 1, Nk * Nframe * log2(M));% 发送的是bit(根据M修改生成的比特数)frame_FDparallel = reshape(frame_FDserial, Nk, Nframe * log2(M)); % 串并转换frame_mod = QAMMod(frame_FDparallel, Nk, Nframe, M); %调制(改为QAM调制)% IFFTpower_FT = 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* log2(M));x = 1:1:160;% Channel         power_TDserial = sum(abs(frame_TDserial).^2) / length(frame_TDserial); % 计算发送序列的能量EsN0 = EbN0 + 10 * log10(log2(M)); % 根据信噪比计算Es/N0(dB)噪声能量,幅值,然后加在信号上N0 = power_TDserial / (10^(EsN0 / 10));noise_msg = sqrt(N0 / 2) .* (randn(size(frame_TDserial)) + 1i * randn(size(frame_TDserial)));frame_recieved = frame_TDserial + noise_msg;% 接收端,串并转换frame_recieved_parallel = reshape(frame_recieved, Nsym, Nframe* log2(M));% 去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 = QAMDemod(frame_recieved_FD, Nk, Nframe, M); %改为QAM解调% 并串转换frame_output = reshape(frame_demod, 1, Nk * Nframe * log2(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);
endsemilogy(EbN0s, bers, '-+');
xlabel('比特信噪比');
ylabel('误码率');
title('不同信噪比下误码率仿真曲线');
legend(strcat(num2str(M),'QAM实验曲线'));function outs = QAMMod(input_data,nk,nframe,M)
% 输入
% input_data: 待数字调制的输入数据(Nk,Nframe*log2(M))
% nk: 子载波个数,也就是并联个数
% nframe: 一帧中包含多少OFDM符号
% M: 调制数
% 输出
% outs: (nk,nframe),输出a+bi
% out_coss:(nk,nframe),输出实部
% out_sins:(nk,nframe),输出虚部if nargin < 4                   % 设置默认值M = 4; % 默认为4QAM
end% 将输入二进制数据映射成QAM符号
symbols = qammod(input_data, M);% 将QAM符号按照OFDM格式进行串并转换
outs = reshape(symbols, nk, nframe* log2(M));
endfunction outputs = QAMDemod(input_data,nk,nframe,M)
% 输入
% input_data: (Nk, Nframe), 一个频域的复数,会被拆开解调
% nk: 频域并联
% nframe: 一帧包含符号数
% M: 调制数
% 输出
% outputs:(Nk, Nframe*log2(M)), 解调后的比特流if nargin < 4                   % 设置默认值M = 4; % 默认为4QAM
end% 将输入QAM符号按照OFDM格式进行并串转换
symbols = input_data(:);% 将QAM符号解调为二进制比特流
outputs = qamdemod(symbols, M);
endfunction output_TD = AddGI(input_TD, nfft, nGI, nframe, type_GI)
if type_GI=="CP"    % 实现CPoutput_TD = [input_TD(nfft-nGI+1:nfft, :); input_TD(1:nfft, :)];
elseif type_GI=="ZP" % 实现ZPoutput_TD = [zeros(nGI,nframe); input_TD(1:nfft, :)];
end
endfunction output_TD = RemoveGI(input_TD,nfft,nGI)
% 输入
% input_TD: (Nsym,Nframe)输入的并联时域数据
% nfft:fft长度
% nGI: GI长度
% 输出
% output_TD: (Nfft,Nframe)去掉GI后的并联时域数据output_TD = input_TD(nGI+1:nfft+nGI,:);
end

在这里插入图片描述

2 瑞利衰落信道之TDL信道

时延抽头延迟线(TDL,Tap Delay Line)信道是一种简化的多径模型,用于表示无线通道在特定延迟时间内的衰减特性。该模型通过一系列的时变复数增益,也称为"抽头"来表示信号经不同路径传播后的相位变化和衰减。在给定的TDL模型中,每个抽头都对应着一个特定的时延和相应的平均功率衰减。

信道的数学模型可以表示为:
y ( t ) = ∑ i = 1 N h i ⋅ x ( t − τ i ) + n ( t ) y(t) = \sum_{i=1}^{N} h_i \cdot x(t - \tau_i) + n(t) y(t)=i=1Nhix(tτi)+n(t)

其中,

  • ( y(t) ) 是接收信号,
  • ( x(t) ) 是发送信号,
  • ( h_i ) 是第 ( i ) 个抽头的复数增益,反映了信号在抽头 ( i ) 上的衰减和相位变化 h i h_i hi 取以 $ h_i = \sqrt{\text{PowerTDL}_i} \cdot \text{Rayleigh fading factor}$,并且 ( h ) 数组中的非零位置由TDL抽头的时延决定,则 $\text{Delay}[i]+1) = h_i $
  • $ \tau_i$ 是第 i 个抽头的时间延迟,
  • N是总抽头数,
  • n(t) 是添加到通道输出的高斯白噪声。

在给定的信道模型中,PowerTDL_dB定义了各抽头的平均功率衰减,单位为分贝(dB),Delay数组定义了各抽头相对于首个抽头的时延,单位为符号周期。这两个数组被用来计算PowerTDL,即各抽头的功率,并转换为线性单位。通过这些值,以及Rayleigh衰减模型,可以生成模拟多径效应的复数信道响应。

2.1 BPSK和QPSK

%% 设置参数
clear;clc;
Nk = 128;           % 子载波个数
Nfft = 128;          % fft长度
Nframe = 6;         % 一帧中有几个OFDM符号
M = 1;              % 调制符号所含比特
SR = 250000;        % 符号速率
BR = SR .* M;       % 比特率
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 = randi([0 1], 1, Nk * Nframe * M);% 发送的是bitframe_FDparallel = reshape(frame_FDserial,Nk,Nframe*M);% 串并转换if M==1frame_mod = BPSKMod(frame_FDparallel, Nk, Nframe); % BPSK调制elseif M==2frame_mod = QPSKMod(frame_FDparallel, Nk, Nframe); % QPSK调制end% 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);
%--------------------------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(M);                                  % 根据信噪比计算噪声能量,幅值,然后加在信号上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;
%--------------------------接收端-------------------------------%% 接收端,串并转换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);% 信道均衡H = fftshift(fft([h zeros(1, Nfft-Tau_maxTDL)].', Nfft));frame_equalization = frame_recieved_FD ./ repmat(H, 1, Nframe);if M==1% BPSK解调frame_demod = BPSKDemod(frame_equalization, Nk, Nframe);elseif M==2% QPSK解调frame_demod = QPSKDemod(frame_equalization, Nk, Nframe);end% 并串转换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
%% 画图
semilogy(EbN0s,bers,'-+');
xlabel('比特信噪比');
ylabel('误码率');
title('不同信噪比下误码率仿真曲线');
% legend('理论曲线','实验曲线');
if M==1legend('BPSK实验曲线');
elselegend('QPSK实验曲线');
endfunction outs = QPSKMod(input_data,nk,nframe,m)
% 输入
% input_data: 待数字调制的输入数据(Nk,Nframe*M)
% nk: 子载波个数,也就是并联个数
% nframe: 一帧中包含多少OFDM符号
% m: 调制数
% 输出
% outs: (nk,nframe),输出a+bi
% out_coss:(nk,nframe),输出实部
% out_sins:(nk,nframe),输出虚部if nargin < 4                   % 设置默认值m = 2;
endouts = zeros(nk,nframe);    % 初始化
out_cos = zeros(nk,nframe);
out_sin = zeros(nk,nframe);input_data_pn = 2*input_data - 1;     %01映射到-11
A = 1/sqrt(2);                        % 归一化幅值out_cos((1:nk),(1:nframe)) = input_data_pn((1:nk), (1:2:2*nframe-1)) .* A;    % 每次使用两列
out_sin((1:nk),(1:nframe)) = input_data_pn((1:nk), ((2:2:2*nframe))) .* A;outs = out_cos + out_sin * 1j;
endfunction outputs = QPSKDemod(input_data,nk,nframe)
% 输入
% input_data: (Nk, Nframe), 一个频域的复数,会被拆开解调
% nk: 频域并联
% nframe: 一帧包含符号数
% 输出
% outputs:(Nk, 2*Nframe), 解调后,多出一倍,全是01
outputs = zeros(nk,2*nframe);
A = 1/sqrt(2); 
input_data = input_data ./ A;
outputs((1:nk),(1:2:2*nframe-1)) = real(input_data((1:nk),(1:nframe)))>=0;
outputs((1:nk),(2:2:2*nframe)) = imag(input_data((1:nk),(1:nframe)))>=0;endfunction outs = BPSKMod(input_data,nk,nframe,m)
% BPSK调制函数
% 输入
% input_data: 待数字调制的输入数据(Nk,Nframe*M)
% nk: 子载波个数,也就是并联个数
% nframe: 一帧中包含多少OFDM符号
% m: 调制数,默认值为1
% 输出
% outs: (nk,nframe),输出复数
A = 1/sqrt(2);    % 归一化幅值
outs = input_data * 2 - 1;  % 将二进制0/1映射为复数-1/1
outs = A * outs;  % 乘以归一化幅值
endfunction outputs = BPSKDemod(input_data,nk,nframe)
% BPSK解调函数
% 输入
% input_data: (Nk, Nframe), 一个频域的复数
% nk: 频域并联
% nframe: 一帧包含符号数
% 输出
% outputs:(Nk, Nframe), 解调后的二进制数据
outputs = real(input_data) >= 0;  % 判断实部是否大于等于0,大于等于0表示1,小于0表示0
end
function output_TD = AddGI(input_TD, nfft, nGI, nframe, type_GI)
if type_GI=="CP"    % 实现CPoutput_TD = [input_TD(nfft-nGI+1:nfft, :); input_TD(1:nfft, :)];
elseif type_GI=="ZP" % 实现ZPoutput_TD = [zeros(nGI,nframe); input_TD(1:nfft, :)];
end
endfunction output_TD = RemoveGI(input_TD,nfft,nGI)
% 输入
% input_TD: (Nsym,Nframe)输入的并联时域数据
% nfft:fft长度
% nGI: GI长度
% 输出
% output_TD: (Nfft,Nframe)去掉GI后的并联时域数据output_TD = input_TD(nGI+1:nfft+nGI,:);
end
function H=Rayleigh_model(nchannel, power_channel)
% 瑞利衰落信道
% 输入
% nchannel: 多径信道的个数
% power_channel:(1, nchannel),每一个信道的功率
% 输出
% H:(1, nchannel),一个瑞利信道,符合高斯分布的nchannel个随机数,代表着衰落
H = (randn(1,nchannel)+1j*randn(1,nchannel)).*sqrt(power_channel/2);
% 功率除以二的原因是瑞利分布的E(x^2)=2\sigma^2
end

在这里插入图片描述

2.2 QAM

%% 设置参数
clear;clc;
Nk = 128; % 子载波个数
Nfft = 128; % fft长度
Nframe = 6; % 一帧中有几个OFDM符号
M = 4; % 调制符号所含比特(改为4:4QAM,16:16QAM,32:32:QAM)
SR = 250000; % 符号速率
BR = SR * log2(M); % 比特率(根据调制方式计算比特率)
NGI = 32; % 保护间隔长度
EbN0s = 0:1:12; % 信噪比
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 = randi([0 1], 1, Nk * Nframe * log2(M));% 发送的是bit(根据M修改生成的比特数)frame_FDparallel = reshape(frame_FDserial, Nk, Nframe * log2(M)); % 串并转换frame_mod = QAMMod(frame_FDparallel, Nk, Nframe, M); %调制(改为QAM调制)% IFFTpower_FT = 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* log2(M));% 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(M);                                  % 根据信噪比计算噪声能量,幅值,然后加在信号上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;% 接收端,串并转换frame_recieved_parallel = reshape(frame_recieved, Nsym, Nframe* log2(M));% 去GIframe_noGI = RemoveGI(frame_recieved_parallel, Nfft, NGI);% FFTframe_recieved_FD_shift = fft(frame_noGI, Nfft);frame_recieved_FD = fftshift(frame_recieved_FD_shift);% 信道均衡H = fftshift(fft([h zeros(1, Nfft-Tau_maxTDL)].', Nfft));frame_equalization = frame_recieved_FD ./ repmat(H, 1, Nframe* log2(M));% QPSK解调frame_demod = QAMDemod(frame_equalization, Nk, Nframe, M); %改为QAM解调% 并串转换frame_output = reshape(frame_demod, 1, Nk * Nframe * log2(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);
endsemilogy(EbN0s, bers, '-+');
xlabel('比特信噪比');
ylabel('误码率');
title('不同信噪比下误码率仿真曲线');
legend(strcat(num2str(M),'QAM实验曲线'));function outs = QAMMod(input_data,nk,nframe,M)
% 输入
% input_data: 待数字调制的输入数据(Nk,Nframe*log2(M))
% nk: 子载波个数,也就是并联个数
% nframe: 一帧中包含多少OFDM符号
% M: 调制数
% 输出
% outs: (nk,nframe),输出a+bi
% out_coss:(nk,nframe),输出实部
% out_sins:(nk,nframe),输出虚部if nargin < 4                   % 设置默认值M = 4; % 默认为4QAM
end% 将输入二进制数据映射成QAM符号
symbols = qammod(input_data, M);% 将QAM符号按照OFDM格式进行串并转换
outs = reshape(symbols, nk, nframe* log2(M));
endfunction outputs = QAMDemod(input_data,nk,nframe,M)
% 输入
% input_data: (Nk, Nframe), 一个频域的复数,会被拆开解调
% nk: 频域并联
% nframe: 一帧包含符号数
% M: 调制数
% 输出
% outputs:(Nk, Nframe*log2(M)), 解调后的比特流if nargin < 4                   % 设置默认值M = 4; % 默认为4QAM
end% 将输入QAM符号按照OFDM格式进行并串转换
symbols = input_data(:);% 将QAM符号解调为二进制比特流
outputs = qamdemod(symbols, M);
endfunction output_TD = AddGI(input_TD, nfft, nGI, nframe, type_GI)
if type_GI=="CP"    % 实现CPoutput_TD = [input_TD(nfft-nGI+1:nfft, :); input_TD(1:nfft, :)];
elseif type_GI=="ZP" % 实现ZPoutput_TD = [zeros(nGI,nframe); input_TD(1:nfft, :)];
end
end
function output_TD = RemoveGI(input_TD,nfft,nGI)
% 输入
% input_TD: (Nsym,Nframe)输入的并联时域数据
% nfft:fft长度
% nGI: GI长度
% 输出
% output_TD: (Nfft,Nframe)去掉GI后的并联时域数据output_TD = input_TD(nGI+1:nfft+nGI,:);
end
function H=Rayleigh_model(nchannel, power_channel)
% 瑞利衰落信道
% 输入
% nchannel: 多径信道的个数
% power_channel:(1, nchannel),每一个信道的功率
% 输出
% H:(1, nchannel),一个瑞利信道,符合高斯分布的nchannel个随机数,代表着衰落
H = (randn(1,nchannel)+1j*randn(1,nchannel)).*sqrt(power_channel/2);
% 功率除以二的原因是瑞利分布的E(x^2)=2\sigma^2
end

在这里插入图片描述

相关文章:

基于MATLAB实现的OFDM仿真调制解调,BPSK、QPSK、4QAM、16QAM、32QAM,加性高斯白噪声信道、TDL瑞利衰落信道

基于MATLAB实现的OFDM仿真调制解调&#xff0c;BPSK、QPSK、4QAM、16QAM、32QAM&#xff0c;加性高斯白噪声信道、TDL瑞利衰落信道 相关链接 OFDM中的帧&#xff08;frame&#xff09;、符号&#xff08;symbol&#xff09;、子载波&#xff08;subcarriers&#xff09;、导频…...

Redis核心技术与实战【学习笔记】 - 21.Redis实现分布式锁

概述 在《20.Redis原子操作》我们提到了应对并发问题时&#xff0c;除了原子操作&#xff0c;还可以通过加锁的方式&#xff0c;来控制并发写操作对共享数据的修改&#xff0c;从而保证数据的正确性。 但是&#xff0c;Redis 属于分布式系统&#xff0c;当有多个客户端需要争…...

17.Golang channel的基本定义及使用

目录 概述实践无缓冲 channel代码结果 缓冲 channel代码结果 channel的关闭特点代码结果range代码结果 select channel代码结果 结束 概述 此篇文章介绍 channel 的用法 无缓冲 channel缓冲 channelchannel的关闭特点range channelselect channel 每一种&#xff0c;配上完整…...

Linux - iptables 防火墙

一. 安全技术和防火墙 1.安全技术 入侵检测系统&#xff08;Intrusion Detection Systems&#xff09;&#xff1a;特点是不阻断任何网络访问&#xff0c;量化、定位来自内外网络的威胁情况&#xff0c;主要以提供报警和事后监督为主&#xff0c;提供有针对性的指导措施和安全…...

如何在FBX剔除Lit.shader依赖

1&#xff09;如何在FBX剔除Lit.shader依赖 2&#xff09;Unity出AAB包&#xff08;PlayAssetDelivery&#xff09;模式下加载资源过慢问题 3&#xff09;如何在URP中正确打出Shader变体 4&#xff09;XLua打包Lua文件粒度问题 这是第371篇UWA技术知识分享的推送&#xff0c;精…...

cesium-测量高度垂直距离

cesium做垂直测量 完整代码 <template><div id"cesiumContainer" style"height: 100vh;"></div><div id"toolbar" style"position: fixed;top:20px;left:220px;"><el-breadcrumb><el-breadcrumb-i…...

Adobe Illustrator CEP插件开发入门指南

引言 Adobe Creative Cloud&#xff08;创意云&#xff09;中的Illustrator作为一款全球领先的矢量图形设计软件&#xff0c;为设计师提供了丰富的功能和无限的创作可能性。为了进一步增强其功能并满足个性化工作流程需求&#xff0c;Adobe引入了Common Extensibility Platform…...

【Spring】自定义注解 + AOP 记录用户的使用日志

目录 ​编辑 自定义注解 AOP 记录用户的使用日志 使用背景 落地实践 一&#xff1a;自定义注解 二&#xff1a;切面配置 三&#xff1a;Api层使用 使用效果 自定义注解 AOP 记录用户的使用日志 使用背景 &#xff08;1&#xff09;在学校项目中&#xff0c;安防平台…...

linux互斥锁:递归锁,非递归锁用法详解

在实际的项目中经常涉及到共享资源,共享资源被多个线程访问会出现竞争现象;为了解决竞争和保护共享资源常用的机制之一就是互斥锁! 互斥锁又分为递归锁和非递归锁,互斥锁默认是非递归锁,也是我们常用的上锁方式。那么什么是递归锁和非递归锁呢? 非递归锁(Non-recursive …...

MacOS安装dmg提示已文件已损坏的解决方法

MacOS安装dmg提示已文件已损坏的解决方法 导致原因是应用没有上传到苹果的appstroe&#xff0c;系统限制了安装&#xff0c;破碎提示是苹果的误导小手段 方法 一 App 在macOS Catalina&#xff08;比较新的系统&#xff0c;例如m1&#xff0c;m2也适用&#xff09;下提示已损坏…...

前端输入框简单实现检测@成员输入

大体逻辑是 给input框添加一个input监听&#xff0c;并判断输入是否为获取当前光标的位置&#xff0c;你输入的肯定在光标之前&#xff0c;且肯定是最后一个input输入的内容换行可以被认为空格&#xff0c;需要进行全局替换判断成功的逻辑分为两部分&#xff0c;前方一般来说是…...

通过与chatGPT交流实现零样本事件抽取

1、写作动机&#xff1a; 近来的大规模语言模型&#xff08;例如Chat GPT&#xff09;在零样本设置下取得了很好的表现&#xff0c;这启发作者探索基于提示的方法来解决零样本IE任务。 2、主要贡献&#xff1a; 提出了基于chatgpt的多阶段的信息抽取方法&#xff1a;在第一阶…...

使用nodejs和html布局一个简单的视频播放网站,但是使用localhost:端口访问html无法加载视频

js代码&#xff1a; // app.js const express require(express); const path require(path); const app express();// 设置静态文件目录&#xff0c;这里假设你的视频文件在public/videos/目录下 app.use(express.static(path.join(__dirname, )));// 设置主页路由&#xf…...

【AG32VF407】国产MCU+FPGA Verilog双边沿检测输出方波

视频讲解 [AG32VF407]国产MCUFPGA Verilog双边沿检测输出方波 实验过程 本次使用使用AG32VF407开发板中的FPGA&#xff0c;使用双clk的双边沿进行检测&#xff0c;同步输出方波 同时可以根据输出的方波检测clk的频率&#xff0c;以及双clk的相位关系&#xff0c;如下为verilog…...

[晓理紫]每日论文分享(有中文摘要,源码或项目地址)--强化学习、模仿学习、机器人

专属领域论文订阅 关注{晓理紫}&#xff0c;每日更新论文&#xff0c;如感兴趣&#xff0c;请转发给有需要的同学&#xff0c;谢谢支持 如果你感觉对你有所帮助&#xff0c;请关注我&#xff0c;每日准时为你推送最新论文。 为了答谢各位网友的支持&#xff0c;从今日起免费为3…...

为什么说TiDB在线扩容对业务几乎没有影响

作者&#xff1a; 数据源的TiDB学习之路 原文来源&#xff1a; https://tidb.net/blog/e82b2c5f 当前的数据库种类繁多&#xff0c;墨天轮当前统计的所有国产数据库已经有 290个 &#xff0c;其中属于关系型数据库的有 166个 。关系型数据库从部署架构上又可以分为集中式…...

STM32--SPI通信协议(2)W25Q64简介

一、W25Q64简介 1、W25Qxx中的xx是不同的数字&#xff0c;表示了这个芯片不同的存储容量&#xff1b; 2、存储器分为易失性与非易失性&#xff0c;主要区别是存储的数据是否是掉电不丢失&#xff1a; 易失性存储器&#xff1a;SRAM、DRAM&#xff1b; 非易失性存储器&#xff…...

svn安装与搭建

1、svn搭建 # yum install subversion -y //安装 # svnserve --version //查看版本 2、创建仓库目录repo # mkdir -p /opt/svn/repo //创建目录 # svnadmin create /opt/svn/repo/ //创建新仓库 # ls !$ …...

什么是缓存击穿、缓存穿透、缓存雪崩?

缓存雪崩 缓存雪崩是指缓存同一时间大面积的失效&#xff0c;所以&#xff0c;后面的请求都会落到数据库上&#xff0c;造成数据库短时间内承受大量请求而崩掉。 解决方案 缓存数据的过期时间设置随机&#xff0c;防止同一时间大量数据过期现象发生。一般并发量不是特别多的时…...

springboot153相亲网站

简介 【毕设源码推荐 javaweb 项目】基于springbootvue 的 适用于计算机类毕业设计&#xff0c;课程设计参考与学习用途。仅供学习参考&#xff0c; 不得用于商业或者非法用途&#xff0c;否则&#xff0c;一切后果请用户自负。 看运行截图看 第五章 第四章 获取资料方式 **项…...

浅谈 React Hooks

React Hooks 是 React 16.8 引入的一组 API&#xff0c;用于在函数组件中使用 state 和其他 React 特性&#xff08;例如生命周期方法、context 等&#xff09;。Hooks 通过简洁的函数接口&#xff0c;解决了状态与 UI 的高度解耦&#xff0c;通过函数式编程范式实现更灵活 Rea…...

遍历 Map 类型集合的方法汇总

1 方法一 先用方法 keySet() 获取集合中的所有键。再通过 gey(key) 方法用对应键获取值 import java.util.HashMap; import java.util.Set;public class Test {public static void main(String[] args) {HashMap hashMap new HashMap();hashMap.put("语文",99);has…...

Qt Widget类解析与代码注释

#include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this); }Widget::~Widget() {delete ui; }//解释这串代码&#xff0c;写上注释 当然可以&#xff01;这段代码是 Qt …...

高频面试之3Zookeeper

高频面试之3Zookeeper 文章目录 高频面试之3Zookeeper3.1 常用命令3.2 选举机制3.3 Zookeeper符合法则中哪两个&#xff1f;3.4 Zookeeper脑裂3.5 Zookeeper用来干嘛了 3.1 常用命令 ls、get、create、delete、deleteall3.2 选举机制 半数机制&#xff08;过半机制&#xff0…...

深入理解JavaScript设计模式之单例模式

目录 什么是单例模式为什么需要单例模式常见应用场景包括 单例模式实现透明单例模式实现不透明单例模式用代理实现单例模式javaScript中的单例模式使用命名空间使用闭包封装私有变量 惰性单例通用的惰性单例 结语 什么是单例模式 单例模式&#xff08;Singleton Pattern&#…...

鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院挂号小程序

一、开发准备 ​​环境搭建​​&#xff1a; 安装DevEco Studio 3.0或更高版本配置HarmonyOS SDK申请开发者账号 ​​项目创建​​&#xff1a; File > New > Create Project > Application (选择"Empty Ability") 二、核心功能实现 1. 医院科室展示 /…...

Psychopy音频的使用

Psychopy音频的使用 本文主要解决以下问题&#xff1a; 指定音频引擎与设备&#xff1b;播放音频文件 本文所使用的环境&#xff1a; Python3.10 numpy2.2.6 psychopy2025.1.1 psychtoolbox3.0.19.14 一、音频配置 Psychopy文档链接为Sound - for audio playback — Psy…...

C++中string流知识详解和示例

一、概览与类体系 C 提供三种基于内存字符串的流&#xff0c;定义在 <sstream> 中&#xff1a; std::istringstream&#xff1a;输入流&#xff0c;从已有字符串中读取并解析。std::ostringstream&#xff1a;输出流&#xff0c;向内部缓冲区写入内容&#xff0c;最终取…...

LLM基础1_语言模型如何处理文本

基于GitHub项目&#xff1a;https://github.com/datawhalechina/llms-from-scratch-cn 工具介绍 tiktoken&#xff1a;OpenAI开发的专业"分词器" torch&#xff1a;Facebook开发的强力计算引擎&#xff0c;相当于超级计算器 理解词嵌入&#xff1a;给词语画"…...

爬虫基础学习day2

# 爬虫设计领域 工商&#xff1a;企查查、天眼查短视频&#xff1a;抖音、快手、西瓜 ---> 飞瓜电商&#xff1a;京东、淘宝、聚美优品、亚马逊 ---> 分析店铺经营决策标题、排名航空&#xff1a;抓取所有航空公司价格 ---> 去哪儿自媒体&#xff1a;采集自媒体数据进…...