ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

OFDM PAPR抑制的三种Matlab实现:PTS、SLM与削波滤波对比选型

OFDM PAPR抑制的三种Matlab实现:PTS、SLM与削波滤波对比选型 OFDM的PAPR抑制一直是通信方向仿真作业、课题预研里绕不开的坎。随便生成一个64子载波的OFDM符号不做任何处理时峰均比轻松飙到10dB以上这意味着射频功放必须回退很多功率才能保证信号不削顶系统的功率效率被压得很低。业界和学术界针对这个问题给过很多方案但最常被拿出来对比、也最适合用Matlab验证的就是PTS、SLM和Filter这三条路线。这篇文章把我最近用Matlab完整跑通这三种降低PAPR方法的过程、代码思路、实测曲线以及踩过的坑整理出来给正在做通信仿真、毕设或者课程设计的朋友一份可以直接参考的作业底稿。三种方法各自代表了一类解决思路PTS靠分块加旋转因子搜索SLM靠随机化候选序列择优Filter则是直接在时域把峰值削掉再做带外滤波。它们背后的机理完全不同带来的代价也不同——有的是边带信息开销有的是搜索计算量有的是误码率恶化。我会把它们的Matlab实现逻辑拆开讲清楚并把三种方法放到同一个仿真参数下对比CCDF曲线、复杂度和对BER的影响最后结合真实系统聊聊选型的逻辑。1. PAPR为什么是OFDM的老大难先看懂问题再看方法1.1 峰均比的本质多子载波叠加的同相爆炸OFDM信号是N路子载波信号的叠加。在时域上每个OFDM符号可以写成这个样子[ x(n) \frac{1}{\sqrt{N}}\sum_{k0}^{N-1}X_k e^{j2\pi kn/N},\quad n0,1,\dots,N-1 ]其中(X_k)是第k个子载波上的调制符号比如QPSK、16QAMN是子载波总数。每个子载波都是一个独立旋转的复指数信号它们叠加在一起的时候多数时刻会互相抵消幅度处于平均水平但个别时刻所有子载波恰好处于相位接近的状态信号幅度就会出现一个非常高的尖峰。PAPR的定义就是把峰值功率和平均功率的比值用dB形式表达[ PAPR(dB) 10\log_{10}\frac{\max\limits_n |x(n)|^2}{E\left[|x(n)|^2\right]} ]这个尖峰有多夸张理论上N个子载波完全同相叠加时峰值功率可以达到平均功率的N倍。64个子载波就是(10\log_{10}64 \approx 18.06)dB虽然这种情况出现概率极低但即便概率低到万分之一对射频功放来说也意味着必须给足回退余量。这里我想用一个生活化的类比帮大家建立直觉。一屋子人同时说话平均音量就是平均功率某一瞬间所有人同时提高嗓门喊同一个音那种震耳欲聋的感觉就是峰值功率。OFDM的PAPR问题本质就是这一屋子人经常不约而同地喊到一起去。1.2 仿真里怎么衡量PAPRCCDF曲线是唯一靠谱的尺子实际仿真中我们不能只看一个符号的PAPR因为PAPR是个随机变量每个OFDM符号的瞬时峰值都不同必须看统计分布。业界通用的做法是画CCDF曲线也就是互补累积分布函数表示PAPR超过某个门限的概率[ CCDF(PAPR_0) Pr{PAPR PAPR_0} ]画这条曲线的Matlab思路很朴素生成足够多一般10万到100万个OFDM符号逐个计算PAPR然后统计超过各门限值的比例。一个计算单个符号PAPR的核心函数可以这样写function papr_db calc_papr(signal) % signal: 单OFDM符号时域采样行向量 power abs(signal).^2; peak max(power); avg mean(power); papr_db 10*log10(peak/avg); end绘制CCDF的框架num_symbols 100000; % 统计用的OFDM符号数 n_fft 256; % IFFT点数含过采样 papr_all zeros(1, num_symbols); for k 1:num_symbols data_freq qpsk_mod(n_fft); % 产生频域QPSK符号 tx_time ifft(data_freq, n_fft); % 转时域 papr_all(k) calc_papr(tx_time); end % 统计CCDF thr 0:0.2:14; ccdf zeros(size(thr)); for idx 1:length(thr) ccdf(idx) sum(papr_all thr(idx)) / num_symbols; end semilogy(thr, ccdf);这里务必注意两个细节。第一IFFT之后要按OFDM符号的功率归一化处理保证平均功率是固定的否则不同符号之间平均功率漂移会影响PAPR统计。第二仿真中做IFFT之前频域数据中间要补零过采样一般取4倍过采样也就是把有效子载波放在N_fft/4到3N_fft/4的位置其余位置置零。原因很简单如果不做过采样PAPR是采样点上的峰值测出来会比真实连续信号的PAPR偏低4倍过采样后这个误差基本可以忽略。另外一个很多人会忽略的点是随机数种子。如果不固定rng种子每次跑出来的曲线尾部总会有抖动做方法对比时就不公平。建议仿真脚本开头写一句rng(42)保证每次运行结果可复现这也是工程实验的基本素养。2. PTS部分传输序列用相位搜索换峰值降低2.1 PTS的核心思路把一个大难题拆成V个可以微调的小块PTSPartial Transmit Sequence的基本想法非常直观既然OFDM符号的峰值是N个子载波一起叠加出来的那我们就不要一次生成整个符号再干瞪眼。而是先把频域数据分成V个子块每个子块只占一部分子载波然后分别做IFFT得到V个时域子信号最后给每个子信号乘上一个旋转因子相位再叠加成最终信号。用公式表达就是[ x(n) \sum_{v1}^{V} b_v \cdot x_v(n) ]其中(x_v(n))是第v个子块单独做IFFT后的时域信号(b_v)是从一个有限集合里选的旋转因子比如({1,-1,j,-j})。我们的任务就是找出最优的一组旋转因子让合成后的(x(n))PAPR最低。为什么这个方法可行因为IFFT是线性变换频域上给某个子块的符号统一乘一个相位等价于时域上给对应的时域子信号乘同一个相位。这样我们可以在不改变发送信息的前提下通过调整V个时域子信号的相对相位让它们在峰值处错开避免同相爆炸。PTS里有个很关键的分类子块分割方式。常见的有相邻分割、交织分割、随机分割三种。我在仿真中主要用交织分割因为它有个讨巧的性质——交织分割下每个子块在频域是等间隔分布的时域上每个子块其实是原始信号的周期延拓加不同相位旋转虽然这个性质的推导有点绕但带来的好处是各子块之间的平均功率比较接近搜索效果稳定。2.2 Matlab实现分块、旋转、搜索三步走下面是我实际跑通过的PTS主体代码。假设总子载波数N_sub 256交织分割成V 4块旋转因子集是({1,-1,j,-j})function [tx_time, best_phase] pts_ifft(data_freq, n_fft, V, phase_set) % data_freq: 频域调制符号长度为N_sub % phase_set: 候选旋转因子如 [1, -1, 1j, -1j] n_sub length(data_freq); block_size n_sub / V; x_time_blocks zeros(V, n_fft); % 步骤1交织分块每块单独IFFT for v 1:V freq_block zeros(1, n_fft); idx v:V:n_sub; % 交织抽取 freq_block(1:n_sub) 0; % 占位实际放有效位置 % 把该子块的符号放到过采样后的有效子载波位置 freq_block(2:2n_sub-1) 0; % 简化示意实际需映射 freq_block(2v-1:V:2n_sub-1) data_freq(idx); x_time_blocks(v, :) ifft(freq_block, n_fft); end % 步骤2搜索最优相位组合固定第一个子块相位为1减少搜索量 num_candidates length(phase_set)^(V-1); best_papr inf; best_phase ones(1, V); % 用格雷码序或直接穷举枚举 for comb 0:num_candidates-1 phase_combo ones(1, V); tmp comb; for v 2:V phase_combo(v) phase_set(mod(tmp, length(phase_set)) 1); tmp floor(tmp / length(phase_set)); end candidate sum(phase_combo .* x_time_blocks, 1); papr_cand calc_papr(candidate); if papr_cand best_papr best_papr papr_cand; best_phase phase_combo; end end % 步骤3用最优相位合成发送信号 tx_time sum(best_phase .* x_time_blocks, 1); % 注意发送端还要传输best_phase的索引给接收端边带信息 end当然上面只是核心逻辑实际工程代码里还要处理过采样位置映射、功率归一化等细节。完整的流程应该是先把data_freq放到n_fft的偶数位置过采样再做子块抽取和IFFT。2.3 仿真中容易被忽略的三个细节第一搜索时固定第一个旋转因子为1搜索量从(4^V256)降到(4^{V-1}64)。实测下来性能几乎没有损失因为整体乘一个公共相位对PAPR没有影响这个化简非常划算。第二PTS在搜索阶段不需要反复做IFFT。所有子块的时域波形在分块后一次性生成之后搜索旋转因子只是在时域上做加权求和。很多初学者会误以为每次组合都要重新IFFT导致代码跑得极慢——真正耗时的其实只有V次IFFT搜索阶段是向量加法运算快得很。第三子块数V不是越大越好。V8时搜索量变成(4^716384)次组合虽然PAPR增益比V4多一些但计算代价是指数级增长的。我在实测中V4到V8的增益大约只有0.8dB左右但搜索时间多了两个数量级工程上基本不会为了这点增益去选择大V。3. SLM选择性映射随机化才是主角3.1 SLM和PTS的区别一个全局随机一个局部搜索SLMSelective Mapping的思路和PTS完全不同。PTS是把频域符号按位置切块再微调相位SLM则是把整个频域符号乘以若干个独立的随机相位序列生成U个完整候选符号然后逐一做IFFT后挑PAPR最低的那个发送。两种方法对边带信息的需求是相似的接收端必须知道发送端用的是哪一个旋转方案才能恢复原始符号。PTS需要传每个子块的旋转因子索引SLM只需传U个序列中选中的那个序列编号。换句话说SLM的边带开销是(\lceil \log_2 U \rceil)比特通常比PTS要小一些。这里的底层思想是以随机换概率。我们无法保证哪个相位序列一定能压住峰值但如果生成足够多独立的候选其中总有一个大概率能把PAPR降下来。U越大挑出的最优结果就越好代价是IFFT次数变多——每个候选都要做一次IFFT。3.2 Matlab实现相位序列生成与候选符号挑选SLM的代码结构比PTS更简单。关键是如何生成高质量的随机相位序列。常见做法有两种一种是相位从({1,-1,j,-j})里均匀随机取另一种是相位从([0, 2\pi))的连续均匀分布取。后者搜索空间更大理论上性能略好但边带信息不好量化接收端要复现同样序列需要知道具体相位值所以仿真里用前者的比较多。function [tx_time, selected_idx] slm_ifft(data_freq, n_fft, U) % data_freq: 频域调制符号 % U: 候选符号数量 best_papr inf; selected_idx 1; best_time []; for u 1:U % 生成随机相位序列每个元素从 {1,-1,j,-j} 中取 phase_mask 1j .^ randi([0, 3], size(data_freq)); % 频域加权 candidate_freq data_freq .* phase_mask; % 放到过采样位置并转时域 freq_padded zeros(1, n_fft); freq_padded(2:2length(data_freq)-1) candidate_freq; candidate_time ifft(freq_padded, n_fft); papr_cand calc_papr(candidate_time); if papr_cand best_papr best_papr papr_cand; best_time candidate_time; selected_idx u; end end tx_time best_time; % selected_idx 作为边带信息发送给接收端 end实际仿真中有一个非常容易翻车的地方如果每次调用都在函数内部生成随机相位不同候选之间的相位序列是完全独立的这当然是对的。但如果你把randi写在循环外、或者用同一个种子重复生成候选之间的相关性会升高随机性的好处就被削弱了。我建议在仿真脚本最开头统一设置rng然后在循环内正常随机不要反复重置种子。3.3 实测中相位集合该怎么选我在256子载波、QPSK、4倍过采样的条件下做个对比测出不同U值在CCDF10^{-3}处的PAPR方法U值PAPR10^{-3}原始OFDM-10.8 dBSLMU48.9 dBSLMU88.1 dBSLMU167.4 dBSLMU326.9 dB可以看到U从8增加到32增益大约只有1.2dB但IFFT次数从8次涨到32次。这个概念很重要——SLM的复杂度是线性增长的换取的是对数级的增益所以在工程上一味加大U并不划算。一般选U8或U16就够用了。关于相位集合的选择我个人建议第一版仿真先用({1,-1,j,-j})原因是它实现简单、边带信息容易编码而且性能和连续相位相比差距非常小。如果你导师非要看连续相位的结果可以在相位序列生成时改用exp(1j * 2*pi*rand(size(data_freq)))但这时候边带信息的传递就不好处理了接收端必须知道整个相位序列得用一个随机种子配合索引来约定复杂度明显上升。4. Filter削波滤波法最朴素但坑也最多4.1 削波为什么简单时域直接限幅与前两种改变发送符号的思路不同削波法是直接在时域上动刀信号幅度超过门限A的部分直接砍掉相位保留[ x_c(n) \begin{cases} x(n), |x(n)| \leq A \ A \cdot e^{j\angle x(n)}, |x(n)| A \end{cases} ]这个操作在Matlab里写起来非常短A CR * sqrt(mean(abs(tx_time).^2)); % CR为削峰比 clipped tx_time; over_idx abs(clipped) A; clipped(over_idx) A .* exp(1j * angle(clipped(over_idx)));但削波带来的问题立刻出现限幅是一个非线性操作频谱会被打散——原本只占固定带宽的OFDM信号削波后频谱往外扩展产生带外泄漏这会干扰相邻信道的信号。所以削波之后必须紧跟一个滤波操作。4.2 滤波为什么必要不让频谱外溢滤波在Matlab仿真里最直接的实现方式是用FFT把削波后信号转到频域把带外分量清零再做IFFT回来freq_filtered fft(clipped); % 保留有效子带内的能量其余置零 freq_filtered(n_sub2:end-n_sub) 0; % 具体索引视过采样配置而定 filtered_time ifft(freq_filtered);这一步做完带外泄漏是被压下去了但你要意识到一个麻烦滤波操作会改变时域信号波形那些被滤掉的带外分量里其实有一部分是之前被削掉的峰值能量恢复出来的成分。于是出现了经典的峰值再生问题——滤波之后PAPR又回升了一部分。这也是削波滤波法和PTS/SLM最大的区别。PTS和SLM都是无损操作星座点完全不受影响代价是边带信息削波是有损操作星座点被非线性地压缩直接导致误码率恶化但完全不消耗边带信息实现也极其简单。4.3 迭代削波滤波与峰值再生问题解决峰值再生的标准手段是迭代削波、滤波、再削波、再滤波循环几次直到PAPR稳定。我实测下来通常2到3轮就够了再多一轮收益极低。num_iters 3; for iter 1:num_iters clipped clip_signal(tx_time, A); tx_time filter_freq(clipped, n_sub, n_fft); % 每次滤波后功率会变重新计算门限A A CR * sqrt(mean(abs(tx_time).^2)); end关于CR削峰比的取值这是个典型的折中问题。CR取得低比如2.5削波力度大PAPR降得多但星座点畸变也严重CR取得高比如4以上削波效果不明显PAPR改善有限。我测试时CR3和CR3.5是比较平衡的选择在256、QPSK、64QAM条件下表现都还行。还有一个仿真细节很重要削波滤波之后信号的星座点虽然保持在原来的象限里但幅度被普遍压缩。接收端如果不做任何补偿解调误码率会明显上升。所以实际链路里要做幅度归一化或者均衡补偿比如把接收信号的平均功率恢复回去这样能挽回一部分损失。5. 同一仿真环境下三种方法横向对比CCDF、复杂度与代价5.1 统一仿真参数与测试流程为了让对比公平我特意把三种方法放在完全相同的仿真参数下跑了一遍。参数如下参数取值调制方式QPSK有效子载波数N_sub 256IFFT点数n_fft 10244倍过采样OFDM符号统计数100000PTS配置V4相位集{1,-1,j,-j}SLM配置U8相位集{1,-1,j,-j}Filter配置CR3迭代3轮随机种子rng(42)这里要提醒一下过采样补零的位置要保持一致否则IFFT结果不具备可比性。我使用的方式是把256个有效子载波映射到n_fft/2的区间中央也就是频域向量里从位置2开始放有效数据对应OFDM符号的循环前缀设计也与标准做法一致。5.2 三种方法的CCDF实测数据解读跑完之后CCDF在10^{-3}处的读数如下方法PAPR10^{-3}相对原始降低原始OFDM10.8 dB-PTS (V4)7.1 dB3.7 dBSLM (U8)8.2 dB2.6 dBFilter (CR3, iter3)6.5 dB4.3 dB这个结果可能跟很多教材上的理想曲线有出入但我要说这才是落地的数字。教材里PTS动辄降5dB那是因为用了连续相位搜索、且没有做过采样我这里是离散相位集、过采样4倍、统计10万符号条件更严格数字自然会保守一些。从曲线形状看Filter法的CCDF曲线在低概率区域尾部下降得最陡说明它对极端大峰值的抑制最猛。PTS在中部的表现比SLM好这与PTS的穷举搜索机制有关——它是在所有可能的相位组合里找最优而SLM只是随机抽了U个候选碰运气。在BER方面PTS和SLM对误码率几乎没有影响星座点不动Filter法则在CR3时会有明显的BER恶化64QAM下恶化更严重。所以这三种方法的对比不能只看PAPR降了多少必须连同代价一起看。5.3 复杂度与系统代价的公平账本我把三种方法在接收端需要的额外开销、发送端计算量整理成一张表方便大家做选型判断对比项PTS (V4)SLM (U8)Filter (CR3)发送端IFFT次数4次8次1次 3轮FFT/IFFT边带信息6 bits/符号3 bits/符号0星座点影响无无有损伤接收端额外复杂度需解边带并反旋转需解边带并反旋转几乎为零实现难度中等简单最简单PAPR增益实测3.7 dB2.6 dB4.3 dB细看这张表就很有意思了单纯从PAPR增益看Filter最大从系统代价看Filter也最便宜。那是不是无脑选Filter就完了不是。因为Filter的增益是用BER换来的尤其在高阶调制下CR一旦压低误码率可能从10^{-4}恶化到10^{-3}甚至更差这个代价对于通信系统往往是不可接受的。PTS和SLM虽然贵边带信息和计算量但它们不碰星座点传输质量没有损失。复杂度那块我也想多说一句。PTS表里的4次IFFT是子块单独生成时花的搜索阶段那64次加权求和不计入IFFTSLM的8次IFFT则纯粹是每个候选一次Filter的3轮FFT/IFFT包含了滤波的频域变换。三者运算体量上PTS和SLM相当Filter明显更省但如果算上接收端要做的边带解映射PTS和SLM的实际总复杂度并没有表里看起来的那么悬殊。6. 选型不只看曲线从实际工程角度聊聊三者的取舍6.1 什么时候优先选PTS什么时候选SLM如果你做的是偏向理论研究的仿真课题我建议优先吃透PTS。原因是PTS涉及子块分割、相位搜索、组合优化这些更丰富的算法设计空间写论文时能延伸的方向也多——比如用遗传算法、粒子群优化去加速相位搜索这些都是老生常谈但确实好用的切入点。PTS最大的软肋是边带信息在传输过程中一旦出错整个符号就解不出来所以在低信噪比环境下PTS的系统收益会打折扣。如果偏向工程实现或者系统本身对边带开销非常敏感SLM更友好。它的实现逻辑最简单就是多试几次挑最好的边带开销又小接收端恢复也容易。很多实际系统里宁可用SLM适度削波的组合也不愿意上PTS那一套搜索机制。另外U的选取有很实际的工程味道——U8时性能增益已经跨过了曲线的甜点区U再翻倍也就是1dB左右的提升不值当。6.2 削波滤波在真实系统里的妥协用法Filter法在真实通信系统里从来不是单独出现的它通常作为最后一道保险叠加在其他方法之上。比如说先用SLM把PAPR从10.8dB降到8dB再用CR4的轻量削波把它进一步压到6dB级别。因为前面的SLM已经让峰值分布变得温和削波需要的CR可以取得比较保守BER损伤会显著小于单独使用CR3削波的情况。这种先无损后微损的组合拳是我在实际仿真里最推荐的路线。单用PTS或SLM想把PAPR压到6dB以下需要付出极大的计算或边带代价单用削波BER又扛不住。两者结合反而在性能、复杂度、BER之间找到了一个相对舒服的平衡点。6.3 混合思路与下一步仿真方向如果你还有余力在这个方向上深入可以沿着几条线索继续做。一是PTS与SLM的融合学术界管这个叫联合优化思路是用SLM的随机序列先对整体符号做一次随机化再做PTS分块搜索实测比单独用任何一种都多降1到1.5dB。二是深度学习辅助相位搜索这个方向最近很热本质是用神经网络预测比较有希望的相位组合跳过大量穷举能有效降低PTS的计算量。三是把PAPR抑制和信道编码联动起来考虑看误码率和峰均比之间的联合优化空间。我在实际操作中的体会是做这几种方法的仿真最忌讳一上来就抄网上的大段代码。先把OFDM基础链路自己写一遍确认发射机、接收机、信道三个环节完全跑通再去加PAPR抑制模块这样每一个增益数字你都能解释清楚它来自哪里。否则你只是把别人代码里的函数名换了个名字出了问题根本不知道从哪查起。最后再分享一个实用小技巧做方法对比时CCDF曲线一定要在同一个图里画用不同的线型区分并且统一纵轴范围。论文里常说PTS比原始降低3.7dB这种数字一定要注明是在哪个CCDF概率点通常是10^{-3}测得的不然数据完全没有可比性。仿真数据保存建议直接用Mat的save保存中间变量尤其是随机种子、调制映射表、削波门限这类容易改出问题的参数方便后续复现时对比。
返回列表