简介:这份资源面向无线通信与OFDM系统方向的学习者和研究者,聚焦降低信号峰均功率比(PAPR)这一经典难题,围绕SLM(选择性映射)与PTS(部分传输序列)两种技术的单独实现及联合策略展开,并涉及PSK、QAM调制方式下的性能分析。压缩包内共1个文件,为MATLAB脚本(.m格式),整体约1KB,体积轻量,便于直接运行与二次修改,可用于仿真验证不同候选符号数量、子载波分组策略对PAPR抑制效果的影响。已有194人学习下载,说明该方向具备一定关注度。读者可借助脚本理解SLM与PTS的算法流程、相位旋转因子选取及组合搜索思路,并在此基础上调整参数,观察PAPR分布与计算复杂度的折中关系,为课程设计、论文复现或工程预研提供可运行的参考代码。
1. SLM-PTS 联合抑制 QAM 峰均比:这套组合拳到底值不值得打
做 OFDM 基带链路的人,迟早会被 PAPR 这个指标卡一次脖子。QAM 调制阶数越高、子载波数越多,时域信号叠加后那几个尖峰就越离谱,功放一进非线性区,EVM 和 ACLR 直接崩盘。SLM 和 PTS 是两类最经典的峰均比抑制手段:SLM 靠多组相位序列挑最低峰均比的那一路发,PTS 把子载波分块后给每块配相位因子再拼回去。单独用各有短板——SLM 要传边带信息、PTS 搜索复杂度随分块数指数涨。把两者串起来做 SLM-PTS,本质是用 SLM 的候选集合给 PTS 提供更多相位组合空间,在可接受的复杂度下把 PAPR 再压 1~2 dB。这套方案适合已经跑通 OFDM 基带、正在为功放回退发愁的工程师,也适合做链路级仿真的同学拿来对比算法边界。下面按“先立住原理、再跑通最小链路、最后踩坑”的顺序讲透。
2. SLM 与 PTS 各自在 QAM-OFDM 里干了什么
2.1 从 QAM 星座到时域尖峰:PAPR 是怎么长出来的
QAM-OFDM 的 PAPR 问题根源在于多载波叠加。假设 N 个子载波,每个子载波上承载 16QAM 或 64QAM 符号,IFFT 之后时域样点是 N 个复正弦的加权和。当这些正弦相位偶然对齐,瞬时功率可以达到平均功率的 N 倍量级。PAPR 定义为峰值功率与平均功率之比,工程上更常用 CCDF 来描述:P(PAPR > PAPR0) 随 PAPR0 增大而下降,我们关心的就是给定概率下能压到多少 dB。
QAM 阶数本身不直接决定 PAPR 上界,但高阶 QAM 的星座点幅度分布更分散,IFFT 后峰均比的统计分布会略微恶化。真正的大头是子载波数 N 和调制方式带来的符号随机性。实测中 64QAM、1024 子载波的 OFDM 符号,PAPR 超过 10 dB 的概率在 1e-3 量级,这对功放是灾难。
SLM 的思路很直接:同一组频域数据,乘上 M 个不同的相位旋转序列,得到 M 个候选时域信号,选 PAPR 最低的那个发。接收端需要知道用了哪个序列,所以要么传边带信息,要么用盲检测。PTS 则是把 N 个子载波分成 V 个互不重叠的子块,每块独立 IFFT,然后给每块乘一个相位因子,再叠加。相位因子通常取 {+1, -1, +j, -j} 这类有限集合,搜索使叠加后 PAPR 最小的组合。
两者结合的逻辑是:SLM 提供 M 个全局相位旋转后的候选,每个候选再走 PTS 的子块相位优化,相当于在更大的解空间里找低峰均比信号。代价是复杂度从 M 次 IFFT 变成 M×V 次 IFFT 加组合搜索。
2.2 SLM-PTS 联合结构的信号流与边带信息处理
联合结构有两种常见串法。第一种是 SLM 在外、PTS 在内:先对频域符号乘 SLM 相位序列,再做 PTS 分块和相位优化。第二种是 PTS 在外、SLM 在内:先做 PTS 分块优化,再对结果做 SLM 候选选择。从 PAPR 抑制效果看,第一种更常见,因为 SLM 的全局旋转不破坏 PTS 的子块结构,实现上更干净。
信号流大致是:QAM 映射 → 串并转换 → SLM 相位旋转(M 组)→ 每组做 PTS 分块 → 每块 IFFT → 相位因子搜索 → 叠加 → 选 PAPR 最低 → 加 CP → 发射。接收端需要恢复 SLM 序列索引和 PTS 相位因子。边带信息可以显式传输,比如用预留子载波或单独的控制信道;也可以用盲检测,比如利用 QAM 星座的有限性做最大似然估计。工程上为了稳,多数方案还是显式传边带,代价是频谱效率掉一点。
这里有个容易翻车的点:SLM 和 PTS 的边带信息要一起编码,不能各传各的。如果 SLM 索引和 PTS 相位因子分开传,接收端组合恢复时容易出错,尤其是低 SNR 下。常见做法是把两者打包成一个索引,用纠错码保护后映射到预留子载波上。
2.3 相位序列集合怎么选:随机、Hadamard 还是 Zadoff-Chu
SLM 的相位序列集合直接决定 PAPR 抑制能力。理论上随机相位序列的 CCDF 性能最好,但接收端盲检测难度大。Hadamard 序列正交性好,实现简单,但候选之间的相关性偏高,PAPR 抑制会打折扣。Zadoff-Chu 序列恒幅零自相关,适合做同步,但作为 SLM 相位序列时候选多样性不如随机序列。
工程上我一般这样选:如果接收端能拿到边带信息,直接用随机相位序列,M 取 4 到 8 就够,再大边际收益递减。如果要做盲检测,用 Hadamard 或固定码本,M 取 2 到 4,靠 PTS 的子块相位搜索补性能。PTS 的相位因子集合通常取 {+1, -1, +j, -j},V 取 4 到 8。V 太大搜索复杂度爆炸,V 太小抑制不够。
下面这张表是常见配置的对比,基于链路级仿真经验值,具体数值会随 N 和 QAM 阶数浮动:
| 配置 | SLM M | PTS V | 相位集合 | PAPR 抑制(相对无处理) | 复杂度量级 |
|---|---|---|---|---|---|
| 仅 SLM | 8 | - | 随机 | 3~4 dB | 8 次 IFFT |
| 仅 PTS | - | 8 | {±1,±j} | 3~4 dB | 8 次 IFFT + 搜索 |
| SLM-PTS | 4 | 4 | 随机 + {±1,±j} | 5~6 dB | 16 次 IFFT + 搜索 |
| SLM-PTS | 8 | 8 | 随机 + {±1,±j} | 6~7 dB | 64 次 IFFT + 搜索 |
提示:上表是经验区间,实际抑制效果受子载波数、QAM 阶数和过采样率影响,建议在自己的链路里跑 CCDF 曲线确认。
3. 用 Python 跑通 SLM-PTS 最小链路
3.1 搭 OFDM 基带:QAM 映射、IFFT 与 PAPR 计算
先写一个最小可跑的 OFDM 基带,包含 QAM 映射、IFFT、PAPR 计算。这里用 64QAM、256 子载波、4 倍过采样,过采样是为了捕捉连续时间峰值的近似。
import numpy as np def qam_mod(bits, order=64): """64QAM 映射,输入比特流,输出复符号""" k = int(np.log2(order)) assert len(bits) % k == 0 symbols = bits.reshape(-1, k) # 简化的格雷映射:按二进制转整数后归一化 idx = symbols.dot(1 << np.arange(k)[::-1]) side = int(np.sqrt(order)) real = (idx // side) - (side - 1) / 2 imag = (idx % side) - (side - 1) / 2 return (real + 1j * imag) / np.sqrt((side**2 - 1) * 2 / 3) def ofdm_ifft(freq_symbols, oversample=4): """频域符号补零后 IFFT,过采样""" N = len(freq_symbols) N_os = N * oversample freq_padded = np.zeros(N_os, dtype=complex) freq_padded[:N//2] = freq_symbols[:N//2] freq_padded[-N//2:] = freq_symbols[-N//2:] return np.fft.ifft(freq_padded) * np.sqrt(N_os) def papr_db(time_signal): """计算 PAPR,单位 dB""" peak = np.max(np.abs(time_signal)**2) avg = np.mean(np.abs(time_signal)**2) return 10 * np.log10(peak / avg)qam_mod里做了功率归一化,保证平均符号功率为 1,这样 PAPR 计算不受星座幅度影响。ofdm_ifft用补零方式实现过采样,把 N 点频域数据放到 N×oversample 的 IFFT 输入两端,中间补零。papr_db就是峰值功率比平均功率取 dB。这三个函数是后面所有实验的基础,建议先单独跑一遍确认 QAM 星座和 OFDM 时域波形正常。
3.2 实现 SLM:M 组相位序列与候选选择
SLM 的核心是生成 M 组相位序列,对频域符号做旋转,然后选 PAPR 最低的候选。
def slm_process(freq_symbols, M=4, seed=42): """SLM:生成 M 组随机相位序列,返回最低 PAPR 的时域信号和索引""" rng = np.random.default_rng(seed) N = len(freq_symbols) best_papr = np.inf best_signal = None best_idx = 0 for m in range(M): # 随机相位序列,恒幅 phase = np.exp(1j * 2 * np.pi * rng.random(N)) rotated = freq_symbols * phase time_signal = ofdm_ifft(rotated) p = papr_db(time_signal) if p < best_papr: best_papr = p best_signal = time_signal best_idx = m return best_signal, best_idx, best_paprM是候选数,seed固定后相位序列可复现,接收端如果知道 seed 和索引就能恢复。phase是恒幅随机相位,保证旋转不改变符号功率。返回的best_idx就是边带信息,实际系统里需要传。这里为了简化没有做边带编码,工程实现时要把best_idx用 log2(M) 比特表示并加保护。
3.3 实现 PTS:子块划分、相位因子搜索与叠加
PTS 的关键是子块划分方式和相位因子搜索策略。相邻划分实现简单,交织划分 PAPR 抑制略好但接收端解交织麻烦。这里用相邻划分,相位因子集合 {+1, -1, +j, -j},穷举搜索。
def pts_process(freq_symbols, V=4, phase_set=None): """PTS:相邻划分子块,穷举相位因子组合,返回最低 PAPR 信号和相位组合""" if phase_set is None: phase_set = [1, -1, 1j, -1j] N = len(freq_symbols) block_size = N // V subblocks = [] for v in range(V): blk = np.zeros(N, dtype=complex) start = v * block_size end = start + block_size if v < V - 1 else N blk[start:end] = freq_symbols[start:end] subblocks.append(ofdm_ifft(blk)) best_papr = np.inf best_signal = None best_phases = None # 穷举 4^V 种组合,V=4 时 256 种,可接受 import itertools for phases in itertools.product(phase_set, repeat=V): combined = sum(p * s for p, s in zip(phases, subblocks)) p = papr_db(combined) if p < best_papr: best_papr = p best_signal = combined best_phases = phases return best_signal, best_phases, best_paprblock_size按相邻方式切分,最后一个子块吃掉剩余子载波。每个子块单独 IFFT 后,用itertools.product穷举相位组合。V=4 时 256 种组合,V=8 时 65536 种,复杂度涨得很快。工程上 V 超过 6 就要考虑次优搜索,比如只搜部分组合或用迭代法。返回的best_phases是边带信息,接收端需要知道每个子块用了哪个相位。
3.4 串起来跑:SLM-PTS 联合与 CCDF 对比
把 SLM 和 PTS 串起来,跑 CCDF 曲线对比单独使用和联合使用的效果。
def ccdf_papr(signals, papr_thresholds): """计算 CCDF:P(PAPR > threshold)""" paprs = np.array([papr_db(s) for s in signals]) ccdf = [(paprs > t).mean() for t in papr_thresholds] return ccdf def run_simulation(num_symbols=2000, N=256, M=4, V=4): """跑多次 OFDM 符号,收集 SLM、PTS、SLM-PTS 的 PAPR 样本""" rng = np.random.default_rng(0) papr_slm, papr_pts, papr_joint, papr_none = [], [], [], [] for _ in range(num_symbols): bits = rng.integers(0, 2, N * 6) # 64QAM,每符号 6 比特 freq = qam_mod(bits, 64) # 无处理 papr_none.append(papr_db(ofdm_ifft(freq))) # 仅 SLM _, _, p_slm = slm_process(freq, M=M) papr_slm.append(p_slm) # 仅 PTS _, _, p_pts = pts_process(freq, V=V) papr_pts.append(p_pts) # SLM-PTS 联合:先 SLM 选候选,再对最优候选做 PTS # 简化实现:对每个 SLM 候选做 PTS,取全局最优 best_joint = np.inf for m in range(M): phase = np.exp(1j * 2 * np.pi * rng.random(N)) rotated = freq * phase _, _, p_j = pts_process(rotated, V=V) if p_j < best_joint: best_joint = p_j papr_joint.append(best_joint) return papr_none, papr_slm, papr_pts, papr_joint # 跑仿真并打印平均 PAPR p_none, p_slm, p_pts, p_joint = run_simulation(num_symbols=500) print(f"无处理平均 PAPR: {np.mean(p_none):.2f} dB") print(f"仅 SLM 平均 PAPR: {np.mean(p_slm):.2f} dB") print(f"仅 PTS 平均 PAPR: {np.mean(p_pts):.2f} dB") print(f"SLM-PTS 平均 PAPR: {np.mean(p_joint):.2f} dB")这段代码里run_simulation对每个 OFDM 符号分别跑四种处理。联合部分为了简化,对每个 SLM 候选都做一次 PTS,取全局最优,实际复杂度是 M×V 次 IFFT 加 M 次 PTS 搜索。跑 500 个符号大概几十秒,看平均 PAPR 就能判断联合是否值得。如果联合比单独只低 0.5 dB 以内,而复杂度翻了几倍,就要重新考虑参数。
注意:这里用平均 PAPR 快速对比,正式评估要看 CCDF 曲线在 1e-3 或 1e-4 概率点的值,平均 PAPR 会被低概率事件稀释。
4. 参数怎么调:M、V、过采样与相位集合的取舍
4.1 M 和 V 的边际收益:什么时候加码不再划算
M 和 V 的 PAPR 抑制收益都是递减的。M 从 1 加到 4,平均 PAPR 能降 2~3 dB;从 4 加到 8,再降 0.5~1 dB;从 8 加到 16,基本只剩 0.2~0.3 dB。V 也类似,V=4 到 V=8 有收益,V=16 以上搜索复杂度不可接受。
工程上我一般这样定:如果功放回退要求 PAPR 压到 8 dB 以下,先试 M=4、V=4,看 CCDF 在 1e-3 点能不能达标。不够就把 M 加到 8,V 保持 4,因为 SLM 的复杂度是线性涨,PTS 是指数涨。如果还不够,再考虑 V=8 但改用次优搜索,比如只搜 16 种代表性组合。
下面这张表是不同 M、V 组合下的平均 PAPR 经验值,基于 64QAM、256 子载波、4 倍过采样:
| M | V | 平均 PAPR (dB) | 相对无处理抑制 (dB) |
|---|---|---|---|
| 1 | 1 | 10.8 | 0 |
| 4 | 1 | 7.9 | 2.9 |
| 8 | 1 | 7.2 | 3.6 |
| 1 | 4 | 8.1 | 2.7 |
| 1 | 8 | 7.5 | 3.3 |
| 4 | 4 | 6.8 | 4.0 |
| 8 | 4 | 6.3 | 4.5 |
| 4 | 8 | 6.5 | 4.3 |
| 8 | 8 | 6.0 | 4.8 |
提示:这张表是单次仿真的均值,不同 seed 会有 ±0.3 dB 波动,建议跑多次取平均。
4.2 过采样率对 PAPR 读数的影响:2 倍够不够
PAPR 是连续时间概念,但我们在数字域计算时只能拿到离散样点。如果不过采样,离散样点的峰值会低估真实连续峰值,PAPR 读数偏低。过采样率 2 倍时,PAPR 读数比 4 倍低 0.3~0.5 dB;4 倍和 8 倍差距在 0.1 dB 以内。工程上做算法对比用 4 倍过采样足够,做功放预算时建议 8 倍或直接用连续时间近似公式。
过采样的实现方式也有讲究。补零 IFFT 是最常用的,但补零位置要对:把 N 点频域数据放在 N×oversample 点的两端,中间补零,这样等效于时域插值。如果补零位置错了,会引入镜像分量,PAPR 读数完全不对。我见过有人把频域数据放在中间补零,结果时域信号被调制到高频,PAPR 算出来离谱。
4.3 相位集合的玄学:为什么 {+1,-1} 有时比 {±1,±j} 更稳
PTS 相位因子集合通常用 {+1, -1, +j, -j},因为四个相位在复平面上均匀分布,搜索空间大。但实际跑下来,有些场景下 {+1, -1} 反而更稳。原因是 {±1, ±j} 虽然组合多,但相位因子的选择会导致子块间功率抵消不充分,某些组合下叠加信号峰值反而更高。{+1, -1} 只有两个相位,搜索空间小,但每个子块的功率变化更可控。
另一个玄学点是相位集合和 QAM 阶数的交互。高阶 QAM 星座点密集,相位旋转后的符号间干扰更敏感,用 {±1, ±j} 时接收端均衡器要处理更复杂的相位跳变。如果系统对 EVM 要求严,建议先用 {+1, -1} 跑通,再逐步加相位。
5. SLM-PTS 落地避坑:从边带信息到复杂度的五个翻车点
5.1 边带信息传错:接收端星座整体旋转
现象:接收端解调后星座图整体旋转 90 度或 180 度,误码率飙升。
原因:SLM 索引或 PTS 相位因子在传输中出错,接收端用了错误的相位去恢复。尤其是显式传边带时,如果边带比特没有纠错保护,低 SNR 下容易翻车。
解决:边带信息用 1/2 码率卷积码或 LDPC 保护,映射到预留子载波时功率抬高 3~6 dB。如果做盲检测,用 QAM 星座的有限性做最大似然,但复杂度高,建议只在 M≤4 时用。
5.2 复杂度爆炸:V=8 穷举搜索跑不动
现象:仿真跑一个 OFDM 符号要几秒,V=8 时 65536 种组合,实时系统根本扛不住。
原因:PTS 穷举搜索复杂度是 O(4^V),V 每加 1 复杂度乘 4。V=8 时 65536 次叠加和 PAPR 计算,DSP 上单符号就要几十毫秒。
解决:改用次优搜索。常见做法是迭代法:每次只优化一个子块的相位,固定其他子块,循环几轮。或者用分组搜索:把 V 个子块分成两组,每组独立搜最优,再联合微调。复杂度能降到 O(V×4) 量级,性能损失 0.3~0.5 dB。
5.3 过采样补零位置错:PAPR 读数虚低
现象:PAPR 算出来只有 6 dB,但实测功放还是失真。
原因:过采样 IFFT 补零位置不对,频域数据没有放在两端,导致时域信号被意外调制,峰值被平滑掉。
解决:补零时严格把 N 点频域数据放在 N×oversample 点的前 N/2 和后 N/2 位置,中间补零。写完代码先用单音信号验证:一个子载波置 1,其余置 0,IFFT 后应该是恒幅正弦,PAPR 接近 0 dB。
5.4 相位序列相关性高:SLM 候选 diversity 不足
现象:M=8 但 PAPR 抑制只有 2 dB,远低于预期。
原因:SLM 相位序列之间相关性太高,候选信号彼此太像,选最低 PAPR 也降不下来。随机序列如果 seed 没选好,或者用 Hadamard 序列但没做行置换,都会出现这个问题。
解决:随机序列用不同 seed 生成多组,检查候选之间的互相关系数,确保低于 0.3。Hadamard 序列做行置换后再用,或者直接用 Zadoff-Chu 序列的不同根生成。
5.5 边带信息开销吃掉频谱效率
现象:PAPR 压下来了,但吞吐量反而降了。
原因:SLM 索引 log2(M) 比特加 PTS 相位 V×log2(4) 比特,如果 M=8、V=8,边带信息 3+16=19 比特每符号。256 子载波 64QAM 每符号 1536 比特,边带占 1.2%,看起来不多,但如果用预留子载波传,实际开销更大。
解决:边带信息压缩。SLM 索引和 PTS 相位联合编码,用更少的比特表示。或者用盲检测省掉边带,但接收端复杂度上升。工程上要在频谱效率和 PAPR 抑制之间折中,一般边带开销控制在 1% 以内可接受。
6. 进阶技巧:用 CCDF 曲线和 EVM 联合验证 SLM-PTS 的真实收益
跑通最小链路后,别只看平均 PAPR。真正决定这套方案值不值得上的,是 CCDF 曲线在低概率点的表现和 EVM 的恶化程度。我一般会做三件事。
第一,画 CCDF 对比图。横轴 PAPR 阈值,纵轴 P(PAPR > threshold),取对数坐标。重点看 1e-3 和 1e-4 两个点。如果 SLM-PTS 在 1e-3 点比单独 SLM 低 1 dB 以上,说明联合有效;如果只低 0.3 dB,复杂度翻倍就不划算。
第二,测 EVM。PAPR 抑制本身会引入带内失真,尤其是 PTS 的相位因子搜索是次优时。把处理后的信号过一遍理想解调,算 EVM,确保在 3GPP 或系统要求的门限内。64QAM 通常要求 EVM 低于 8%,256QAM 低于 3.5%。如果 PAPR 压下来了但 EVM 超标,等于白干。
第三,看功放回退的实际收益。PAPR 降 1 dB,功放回退可以少 1 dB,输出功率多 1 dB,覆盖或吞吐量有实际提升。但如果边带开销吃掉 1% 频谱效率,要算净收益。我一般用这个公式快速估算:净收益 = PAPR 抑制带来的功率增益 - 边带开销带来的效率损失。如果净收益低于 0.5 dB,这个方案就不值得上。
下面是一个 CCDF 对比的代码片段,把前面几种处理的 PAPR 样本收集起来画曲线:
import matplotlib.pyplot as plt def plot_ccdf(papr_samples_dict): """画 CCDF 曲线,输入字典 {label: papr_list}""" thresholds = np.linspace(4, 14, 100) for label, paprs in papr_samples_dict.items(): paprs = np.array(paprs) ccdf = [(paprs > t).mean() for t in thresholds] plt.semilogy(thresholds, ccdf, label=label) plt.xlabel('PAPR (dB)') plt.ylabel('P(PAPR > threshold)') plt.grid(True, which='both') plt.legend() plt.show() # 假设已经跑出 p_none, p_slm, p_pts, p_joint # plot_ccdf({'无处理': p_none, 'SLM': p_slm, 'PTS': p_pts, 'SLM-PTS': p_joint})semilogy把纵轴设为对数,方便看低概率尾部。thresholds从 4 到 14 dB 覆盖常见 PAPR 范围。跑完图后重点看 1e-3 线对应的 PAPR 值,那就是工程上要用的功放回退参考。
最后说个我自己的习惯:每次调完 M、V 或相位集合,先跑 1000 个符号的 CCDF,再跑 100 个符号的 EVM,两个都达标才继续。别嫌慢,PAPR 这东西玄学得很,仿真好看实测翻车的事我见过太多次。希望帮到你。
本文还有配套的精品资源,点击获取