赞
踩
目录
二、Pkt detect + Freq Sync + Time Sync + ChaEst
光阴似箭,岁月如梭,还记得当时上完 OFDM 就未再作理会了。如今临界毕业,整理学习资料时发现了 OFDM 相关内容,遂加以整理,并在 CSDN 纪念一下。当时 OFDM 是英文授课的,学习资料同理也都是英文的,加之 OFDM 中有许多专业名词术语、网上资料层次不齐,以致学来一头雾水,不得要领。好在后来自己整理了不少中文学习资料和代码,很好地加深了对 OFDM 的理解,遂感叹 “实践出真知”。而此处将分享某次基于 802.11a 的 OFDM 实验报告稿件 (因此没失踪的原件详细),后续有机会还会上传更多资料。(更新:部分资料链接) (更新:有些遗漏的内容,会根据读者反馈补充到文末的补充材料中,所以不下载根本不影响)
本文主要分为四部分介绍,同时给出呕心沥血的实现 (含注释)。(注:需要 MATLAB 2016a 及更高版本)
- 第一部分:实现了一个 OFDM 基本系统。关键词:前导训练序列 (Preamble training symbol)、长/短训练序列 (Long/Short training symbol)、卷积码、4QAM 调制、导频 (Pilot)、映射 (Mapping) 与逆/解映射 (Demapping)、循环前缀 (CP) 等。
- 第二部分:实现了对 OFDM 系统进行功能拓展。关键词:包检测 (Packet detection)、频率同步、时间精同步、信道估计等。
- 第三部分:将各功能集成到统一的系统中,并给出实现过程及注释。
- 第四部分:根据需求补充的若干函数代码
事实上,学习的原理主要来自《宽带无线通信OFDM技术 - 第二版》和《OFDM移动通信技术原理与应用》这两本书及课件,虽然它们有一定年代了,但基本原理和经典内容等都是通用的、不过时的。网上搜索很容易找到资源下载,建议有不明白的概念就查阅学习一下。
最后,原本是一字一句敲,可惜效率低下,遂用截图代之 T T。不论如何,如能一字一句读懂,必能豁然开朗,希望对读者有帮助 ^ ^ 。
- function detected_packet = FindPacket(rx_signal)
-
- win_size = 700; % 搜索窗大小 (理论上大于噪声长度500就行)
- LTB = 16; % 每个短训练序列符号长度LTB
-
- % 一次性计算所有相邻符号相关性
- xcorr = rx_signal(1:win_size+2*LTB).* ...
- conj(rx_signal(1*LTB+1:win_size+3*LTB)); %(732,1)
- % (1:700+2*16) .* (1*16+1:700+3*16 = 1;732 .* 17:748
- %-------------------------------------------------------------------------
- % 逐对计算所有相邻符号相关性mn(的分子|Cn|)
- Cn_xcorr = zeros(700,1);
- for i = 1:700
- recorder = 0;
- for j = i : (i+2*LTB- 1)
- recorder = recorder + (xcorr(j+1));
- end
- Cn_xcorr(i) = abs(recorder);
- end
- %-------------------------------------------------------------------------
- % 逐对计算所有相邻符号相关性mn(的分母Pn)
- rx_pwr = abs(rx_signal(1*LTB+1 : win_size+3*LTB)).^2 ; %17-748
- Pn_xcorr = zeros(700,1);
- for i = 1:700
- recorder = 0;
- for j = i : (i+2*LTB-1)
- recorder = recorder + rx_pwr(j+1);
- end
- Pn_xcorr(i) = recorder;
- end
- %-------------------------------------------------------------------------
-
- % 一次性计算所有相邻符号相关性mn=|Cn|/Pn
- x_len = length(Cn_xcorr); % 700*1
- mn = Cn_xcorr(1:x_len)./Pn_xcorr(1:x_len);
- plot(mn); % 绘图
-
- % 判断有无检测到包
- threshold = 0.75; % 判决门限值
- thres_idx = find(mn > threshold); % 查询满足条件(相关性大于门限值)元素的id
- if isempty(thres_idx)
- thres_idx = 1;
- else
- thres_idx = thres_idx(1); % 若有多个id满足条件,则取首个作为检测到包的起点
- end
-
- % 根据id取出数据包
- detected_packet = rx_signal(thres_idx:length(rx_signal));
- clc;
- fs = 20e6;
- gi = 1/4;
- fftlen = 64;
- gilen = gi*fftlen;
-
-
- ShortTrain = sqrt(13/6) * [0 0 1+j 0 0 0 -1-j 0 0 0 1+j 0 0 0 -1-j ...
- 0 0 0 -1-j 0 0 0 1+j 0 0 0 0 0 0 -1-j 0 0 0 ...
- -1-j 0 0 0 1+j 0 0 0 1+j 0 0 0 1+j 0 0 0 1+j 0 0].';
-
- short_demap = zeros(64, 1);
- short_demap([7:32 34:59],:) = ShortTrain;
- short_demap([33:64 1:32],:) = short_demap;
- % 将频域的短训练序列转化到时域
- ShortTrain=sqrt(64)*ifft(sqrt(64/52)*short_demap);
- ShortTrain =ShortTrain(1:16);
- transmit=[ShortTrain;ShortTrain;ShortTrain;ShortTrain;ShortTrain;
- ShortTrain;ShortTrain;ShortTrain;ShortTrain;ShortTrain];
-
- phase=zeros(1,1);
- mse=zeros(1,1);
- error = zeros(1,500);
- snr = 0:1:20;
- for snr_idx = 1:length(snr)
- for n = 1:500
- len = length(transmit); % 计算传输信号长度
- noise =sqrt(1/(10^(snr(snr_idx)/10))/2)*( randn(len,1)+j*randn(len,1));
- % 加噪声
- transmit1 = transmit + noise;
- % 加频偏 [0:total_length-1]/fs=nTs ▲f=0.2*fs/fftlen
- cfo = 0.2*fs/fftlen/fs*[0:len-1];
- phase_shift = exp(j*2*pi*cfo).';
- transmit2 = transmit1.*phase_shift; % 将频偏加到传输的信号上
-
- LTE = 16; %长度为16的窗口
- phase=0;
-
- for i=1:(len-LTE)
- %每一个数据与d个数据后的数据共轭相乘,求总和
- phase=phase+transmit2(i).*conj(transmit2(i+LTE));
- end
-
- %求估计出的频偏
- cfo_est = -angle(phase) / (2*LTE*pi/fs);
- %求频偏估计误差
- error(n) = (cfo_est - (0.2*fs/fftlen))/(0.2*fs/fftlen);
- end
-
- mse(snr_idx) = mean(abs(error).^2);
- end
-
- semilogy(snr,mse,'-o');
- xlabel('SNR/dB');
- ylabel('MSE');
- grid on;
- clear all; clc;
- gi = 1/4;
- fftlen = 64;
- gilen = gi*fftlen;
-
- ShortTrain = sqrt(13/6) * [0 0 1+j 0 0 0 -1-j 0 0 0 1+j 0 0 0 -1-j ...
- 0 0 0 -1-j 0 0 0 1+j 0 0 0 0 0 0 -1-j 0 0 0 ...
- -1-j 0 0 0 1+j 0 0 0 1+j 0 0 0 1+j 0 0 0 1+j 0 0].';
-
- short_demap = zeros(64, 1);
- short_demap([7:32 34:59],:) = ShortTrain;
- short_demap([33:64 1:32],:) = short_demap;
- % 将频域的短训练序列转化到时域并进行功率归一化
- ShortTrain=sqrt(64)*ifft(sqrt(64/52)*short_demap);
- ShortTrain =ShortTrain(1:16);
- short_train_blks=[ShortTrain;ShortTrain;ShortTrain;ShortTrain;ShortTrain;
- ShortTrain;ShortTrain;ShortTrain;ShortTrain;ShortTrain];
-
- longTrain = [1 1 -1 -1 1 1 -1 1 -1 1 1 1 1 1 1 -1 -1 1 1 -1 1 -1 1 1 1 ...
- 1 1 -1 -1 1 1 -1 1 -1 1 -1 -1 -1 -1 -1 1 1 -1 -1 1 -1 1 -1 1 1 1 1].';
-
- long_demap = zeros(64, 1);
- long_demap([7:32 34:59],:) = longTrain;
- long_demap([33:64 1:32],:) = long_demap;
-
- % 将频域的长训练序列转化到时域并进行功率归一化
- longTrain=sqrt(64)*ifft(sqrt(64/52)*long_demap);
- % 取长训练序列的后32位作为cp前缀
- long_train_syms = [longTrain(33:64,:); longTrain; longTrain];
- % 构成发送序列
- transmit = [short_train_blks; long_train_syms];
-
- len = length(transmit);
- error = zeros(500,1);
- time_est = zeros(500,1);
- snr = 0:1:10;
- for snr_idx = 1:length(snr)
- for n = 1:500
- noise = sqrt(1/(10^(snr(snr_idx)/10))/2)* ...
- (randn(len,1)+j*randn(len,1));
- transmit1 = transmit + noise; % 加噪声
- i_matrix=zeros(64,1);
- j_matrix=zeros(51,1);
- for j=150:200 % 正确的同步位置在160+32+1处,选择范围包括193
- for i=1:64 % 长训练序列的64位
- % 接受序列与长训练序列共轭相乘
- i_matrix(i)=transmit1(j-1+i).*conj(longTrain(i));
- % 以每一个bit为起始计算出一个和
- j_matrix(j-149)=j_matrix(j-149)+i_matrix(i);
- end
- end
- [a,b] = max(abs(j_matrix)); % 求和最大的,相关程度最高
-
- time_est(n) = 149 + b; % 求计算出的同步位置
- error(n) = time_est(n) - 193; % 估计位置偏差
- end
- end
-
- mse(snr_idx)= mean(abs(error).^2); % 求mse
- semilogy(snr,mse);
- xlabel('SNR/dB');
- ylabel('MSE');
- grid on;
channel_est = mean(freq_tr_syms,2).*conj(LongTrain);
- %% ************************** Preparation part **************************
- clear all; clc;
-
- % 系统参数
- fs = 8e6; % 抽样频率
- ml = 2; % 调制阶数 = 2 —— QPSK调制
- NormFactor = sqrt(2/3*(ml.^2-1));
- gi = 1/4; % 保护间隔比例 = 1/4
- fftlen = 64; % FFT长度 = 64 points/chips
- gilen = gi*fftlen; % 保护间隔/循环前缀长度 = 16 points/chips
- blocklen = fftlen + gilen; % OFDM符号长度 = 80 points/chips
-
-
- % 子载波标号
- DataSubcPatt = [1:5 7:19 21:26 27:32 34:46 48:52]'; % 数据子载波位置标号
- PilotSubcPatt = [6 20 33 47]; % 导频子载波位置标号
- UsedSubcIdx = [7:32 34:59]; % 共用52个子载波
-
-
- % 信道编码参数
- trellis = poly2trellis(7,[133 171]); % 卷积码
- tb = 7*5;
- ConvCodeRate = 1/2; % 码率 = 1/2
-
-
- % 训练序列
- % 短训练序列 (NumSymbols = 52)
- ShortTrain = sqrt(13/6) * [0 0 1+j 0 0 0 -1-j 0 0 0 1+j 0 0 0 -1-j ...
- 0 0 0 -1-j 0 0 0 1+j 0 0 0 0 0 0 -1-j 0 0 0 ...
- -1-j 0 0 0 1+j 0 0 0 1+j 0 0 0 1+j 0 0 0 1+j 0 0].';
- NumShortTrainBlks = 10; % 短训练序列符号数 = 10
- NumShortComBlks = 16*NumShortTrainBlks/blocklen; % 160/80=2
-
- % 长训练序列 (NumSymbols = 52)
- LongTrain = [1 1 -1 -1 1 1 -1 1 -1 1 1 1 1 1 1 -1 -1 1 1 -1 1 -1 1 1 1 ...
- 1 1 -1 -1 1 1 -1 1 -1 1 -1 -1 -1 -1 -1 1 1 -1 -1 1 -1 1 -1 1 1 1 1].';
- NumLongTrainBlks = 2; % 长训练序列符号数 = 2
- %短训练序列加长训练序列共相当于4个OFDM符号
- NumTrainBlks = NumShortComBlks + NumLongTrainBlks;
-
- short_train = tx_freqd_to_timed(ShortTrain); % 把短训练序列从频域转换到时域
- %plot(abs(short_train));
- short_train_blk = short_train(1:16); % 每个短训练序列长度16
- % 共10个短训练序列 -- 总长度为10*16=160
- short_train_blks = repmat(short_train_blk,NumShortTrainBlks,1);
-
- long_train = tx_freqd_to_timed(LongTrain); % 把长训练序列从频域转换到时域
- long_train_syms = [long_train(fftlen-2*gilen+1:fftlen,:); % 加循环前缀
- long_train; long_train];
- % 构成前导训练序列
- preamble = [short_train_blks; long_train_syms];
-
-
- % 包信息
- NumBitsPerBlk = 48*ml*ConvCodeRate;
- % 每个OFDM符号信息量=48个*2(调制阶数,每个数2bit信息)*卷积码效率
- NumBlksPerPkt = 50; % 每个包符号数50
- NumBitsPerPkt = NumBitsPerBlk*NumBlksPerPkt; % 每个包信息量位50*48
- NumPkts = 250; % 总包数250
-
- % 信道与频偏参数
- h = zeros(gilen,1); % 定义多径信道
- h(1) = 1; h(3) = 0.5; h(5) = 0.2; % 3径
- h = h/norm(h);
- CFO = 0.1*fs/fftlen; % 频偏
-
- % 定时参数
- ExtraNoiseSamples = 500; % 包前加额外500长度的噪声
-
- %% ************************** Loop start *************************************
- snr = 0:1:20; % 用于检测的信噪比值
- ber = zeros(1,length(snr)); % 0值初始化误码率
- per = zeros(1,length(snr)); % 0值初始化误包率
-
- for snr_index = 1:length(snr)
- num_err = 0;
- err = zeros(1,NumPkts);
- for pkt_index = 1:NumPkts % 250个包
-
- %% ***************************** Transmitter ********************************
- % 生成信息序列
- inf_bits = randn(1,NumBitsPerPkt)>0; % 生成48*50个信息比特
- CodedSeq = convenc(inf_bits,trellis); % 卷积编码
-
- % 调制
- paradata = reshape(CodedSeq,length(CodedSeq)/ml,ml); % 分为两路:
- ModedSeq = qammod(bi2de(paradata),2^ml)/NormFactor; % 4QAM调制
-
- mod_ofdm_syms = zeros(52, NumBlksPerPkt);
- mod_ofdm_syms(DataSubcPatt,:) = reshape(ModedSeq,48,NumBlksPerPkt);
- %调制后信号48行50列对应子载波id [1:5 7:19 21:26 27:32 34:46 48:52]';
- mod_ofdm_syms(PilotSubcPatt,:) = 1; % 加导频
-
- % 对OFDM符号做Mapping及IFFT(输出64行50列)
- tx_blks = tx_freqd_to_timed(mod_ofdm_syms);
-
- % 加循环前缀
- % 每个OFDM符号后16位重复放在前面做cp
- tx_frames = [tx_blks(fftlen-gilen+1:fftlen,:); tx_blks];
-
- % 并串转换
- tx_seq = reshape(tx_frames,NumBlksPerPkt*blocklen,1); % 50*80
- tx = [preamble;tx_seq]; % 在50个OFDM符号前加前导序列,构成一个包
-
- %% ****************************** Channel *********************************
- FadedSignal = filter(h,1,tx); % 包通过多径信道
- len = length(FadedSignal);
- noise_var = 1/(10^(snr(snr_index)/10))/2;
- noise = sqrt(noise_var) * (randn(len,1) + j*randn(len,1));
- % 加噪声
- rx_signal = FadedSignal + noise;
- %包前侧加500长度的噪声
- extra_noise = sqrt(noise_var) * (randn(ExtraNoiseSamples,1) + ...
- j*randn(ExtraNoiseSamples,1));
- %包后侧加170长度的噪声
- end_noise = sqrt(noise_var) * (randn(170,1) + j*randn(170,1));
-
- % 接收信号
- rx = [extra_noise; rx_signal; end_noise];
-
- % 引入频偏
- total_length = length(rx); % 计算接收信号长度
- t = [0:total_length-1]/fs;
- phase_shift = exp(j*2*pi*CFO*t).'; % 加载波频率偏移
- rx = rx.*phase_shift;
-
- %% ******************************* Receiver *****************************
- % 包检测
- %rx_signal去掉包前噪声的接收信号,pkt_offset包前噪声的偏移量
- rx_signal = FindPacket(rx);
-
- % 频偏估计与纠正
- %rx_signal补偿频率偏移后的接收信号,cfo_est频率偏移量
- rx_signal = FrequencySync(rx_signal,fs); % 注意文末补充材料的 frequencysync.m
-
- % 时间精同步
- % 时间同步位置
- fine_time_est = FineTimeSync(rx_signal, long_train); % 注意文末补充材料的 finetimesync.m
- % Time synchronized signal
- % 期望去掉短训练序列及长序列前cp后,
- % 得到长度即长训练序列64*2个+80*50个OFDM符号
- expected_length = 64*2+80*NumBlksPerPkt;
- % 去掉短训练序列以及长序列前cp
- fine_time_est_end = fine_time_est+expected_length-1;
- sync_time_signal = rx_signal(fine_time_est:fine_time_est_end);
-
- [freq_tr_syms, freq_data_syms, freq_pilot_syms] = ...
- rx_timed_to_freqd(sync_time_signal);
- % freq_tr_syms取出长训练序列48行n_data_syms列
- % freq_data_syms取出信息48行n_data_syms列
- % freq_pilot_syms取出导频4行n_data_syms列
-
- % 信道估计
- % 接收longtrain freq_tr_syms取行!
- % 平均 H(k) = freq_tr_syms * conj(LongTrainingSyms)
- channel_est = mean(freq_tr_syms,2).*conj(LongTrain);
-
- % Data symbols channel correction
- % 扩展信息序列对应的H(k),同接收OFDM符号个数相同
- chan_corr_mat = repmat(channel_est(DataSubcPatt), ...
- 1, size(freq_data_syms,2));
- % 用估计的H(k)共轭乘接受信息序列,得到估计的发送信息序列
- freq_data_syms = freq_data_syms.*conj(chan_corr_mat);
- % 对导频部分做同样的处理
- chan_corr_mat = repmat(channel_est(PilotSubcPatt), ...
- 1, size(freq_pilot_syms,2));
- freq_pilot_syms = freq_pilot_syms.*conj(chan_corr_mat);
-
- % 幅度归一化
- % 信息序列对应的H(k)绝对值平方行求和
- chan_sq_amplitude = sum(abs(channel_est(DataSubcPatt,:)).^2, 2);
- %扩展至估计的发送信息序列列数
- chan_sq_amplitude_mtx = repmat(chan_sq_amplitude, ...
- 1, size(freq_data_syms,2));
- data_syms_out = freq_data_syms./chan_sq_amplitude_mtx; % 幅度归一化
-
- % 对导频序列做同样处理
- chan_sq_amplitude = sum(abs(channel_est(PilotSubcPatt,:)).^2, 2);
- chan_sq_amplitude_mtx = repmat(chan_sq_amplitude, ...
- 1, size(freq_pilot_syms,2));
- pilot_syms_out = freq_pilot_syms./chan_sq_amplitude_mtx;
-
- phase_est = angle(sum(pilot_syms_out)); % 计算导频
- phase_comp = exp(-j*phase_est);
- data_syms_out = data_syms_out.*repmat(phase_comp, 48, 1);
-
- Data_seq = reshape(data_syms_out,48*NumBlksPerPkt,1); % 48*50
-
- % 解调
- DemodSeq = de2bi(qamdemod(Data_seq*NormFactor,2^ml),ml);
- deint_bits = reshape(DemodSeq,size(DemodSeq,1)*ml,1).';
-
- % 卷积码译码
- DecodedBits = vitdec(deint_bits(1:length(CodedSeq)), ...
- trellis,tb,'trunc','hard'); % 维特比译码
- % 误差计算
- err(pkt_index) = sum(abs(DecodedBits-inf_bits)); % 计算误码个数
- num_err = num_err + err(pkt_index);
- end
- ber(snr_index) = num_err/(NumPkts*NumBitsPerPkt); % 误码率
- per(snr_index) = length(find(err~=0))/NumPkts; % 误包率
- end
-
- %% 绘制 SNR-BER 和 SNR-PER曲线
- semilogy(snr,ber,'-b.');
- hold on;
- semilogy(snr,per,'-re');
- xlabel('SNR (dB)');
- ylabel('ERROE');
- grid on;
- legend('BER','PER')
- function [freq_tr_syms, freq_data_syms, freq_pilot_syms] = rx_timed_to_freqd(time_signal)
-
- UsedSubcIdx = [7:32 34:59];
- DataSubcIdx = [7:11 13:25 27:32 34:39 41:53 55:59];
- PilotSubcIdx = [12 26 40 54];
-
- % Long Training symbols
- long_tr_syms = time_signal(1:2*64);
- long_tr_syms = reshape(long_tr_syms, 64, 2);
-
- % to frequency domain
- freq_long_tr = fft(long_tr_syms)/(64/sqrt(52));
- reorder = [33:64 1:32];
- freq_long_tr(reorder,:) = freq_long_tr;
-
- % Select training carriers
- freq_tr_syms = freq_long_tr(UsedSubcIdx,:);
-
- % Take data symbols
- data_syms = time_signal(129:length(time_signal));
-
- data_sig_len = length(data_syms);
- n_data_syms = floor(data_sig_len/80);
-
- % Cut to multiple of symbol period
- data_syms = data_syms(1:n_data_syms*80);
- data_syms = reshape(data_syms, 80, n_data_syms);
- % remove guard intervals
- data_syms(1:16,:) = [];
-
- % perform fft
- freq_data = fft(data_syms)/(64/sqrt(52));
-
- %Reorder pattern is [33:64 1:32]
- freq_data(reorder,:) = freq_data;
-
- %Select data carriers
- freq_data_syms = freq_data(DataSubcIdx,:);
-
- %Select the pilot carriers
- freq_pilot_syms = freq_data(PilotSubcIdx,:);
- function time_syms = tx_freqd_to_timed(mod_ofdm_syms)
- num_symbols = size(mod_ofdm_syms,2);
-
- UsedSubcIdx = [7:32 34:59];
- resample_patt=[33:64 1:32];
-
- syms_into_ifft = zeros(64, num_symbols);
- syms_into_ifft(UsedSubcIdx,:) = mod_ofdm_syms;
-
- syms_into_ifft(resample_patt,:) = syms_into_ifft;
-
- % Convert to time domain
- time_syms = sqrt(64)*ifft(sqrt(64/52)*syms_into_ifft);
- function out_signal = frequencysync(transmit,fs)
- len = length(transmit);
- pha=zeros(1,1);
- D = 16;%长度为16的窗口
- pha=0;
- for i=1:(len-D-20)
- %每个数据与d个数据后的数据共轭相乘,求总和
- pha=pha+transmit(19+i).*conj(transmit(i+D));
- end
-
- cfo_est = -angle(pha) / (2*D*pi/fs);%求估计出的频偏
- cfo = cfo_est/fs*[0:len-1];%加频偏
- %[0:total_length-1]/fs=nTs ▲f=0.2*fs/fftlen
- phase_shift = exp(-j*2*pi*cfo)';
- out_signal= transmit.*phase_shift;%将频偏加到传输的信号上
- function fine_time_est = finetimesync(transmit,longTrain);
- i_matrix=zeros(64,1);
- j_matrix=zeros(71,1);
- for j=150:220 %正确的同步位置在160+32+1处,选择范围包括193
- for i=1:64 %长训练序列的64位
- i_matrix(i)=transmit(j-1+i).*conj(longTrain(i)); %接受序列与长训练序列共轭相乘
- j_matrix(j-149)=j_matrix(j-149)+i_matrix(i); %以每一个bit为起始计算出一个和
- end
- end
- [a,b] = max(abs(j_matrix)); %求和最大的,相关程度最高
- fine_time_est = 149 +b;
赠人玫瑰,手有余香~ 如果觉得有用,请为我点个赞吧~ ^ ^
《OFDM移动通信技术原理与应用 》人民邮电出版社,佟学俭、罗涛,2003
《宽带无线通信OFDM技术 - 第二版》人民邮电出版社,王文博、郑侃,2007
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。