1) 算法在做什么
OFDM 时域信号峰值高,本质原因是大量子载波同相叠加。
"迭代降低"一般不是一步到位,而是:
- 限幅:把时域样值压到门限 A 以内(非线性,会抬升底噪/产生带外)
- 频域滤波:把限幅产生的"带外失真"扔掉(只保留子载波位置的频域分量)
- 迭代:重复 1→2,让带内失真慢慢"收敛",PAPR 逐步下降
代价:每次限幅都会引入 带内失真(EVM 类损伤),因此迭代不是越多越好,要在 PAPR 与 EVM/BER 间折衷。
2) 关键参数与建模假设
| 参数 | 符号 | 典型值 |
|---|---|---|
| 子载波总数 | N | 256 |
| 有效子载波 | N_used | N(或 N-保护 tone) |
| 调制 | M | 16-QAM / QPSK |
| 过采样率(时域) | L | 4 |
| 限幅门限(幅度) | A | 由目标 PAPR₀ 换算 |
| 迭代次数 | N_iter | 3~8 |
| 帧数(Monte Carlo) | N_frame | ≥500(CCDF 才稳) |
限幅门限与 PAPR 的关系
如果你希望"限幅门限对应某个目标 PAPR₀(dB)":
A=σx10PAPR010A = \sigma_x \sqrt{10^{\frac{PAPR_0}{10}}}A=σx1010PAPR0
其中 (σx2=E∣x(n)∣2\sigma_x^2=E\|x(n)\|\^2σx2=E∣x(n)∣2)(用一段训练样本估计,或直接归一化平均功率为 1)。
3) 核心函数:迭代限幅 + 频域滤波(ICF)
这里我用 频域把"非子载波位置置零" 来模拟"只保留有用子载波"------等价于理想的子载波内滤波。
matlab
function x_out = icf_ofdm(y, A, N_iter, Nfft, N_used, cp_len)
% ICF_OFDM Iterative Clipping and Frequency-Domain Filtering
% y : 时域 OFDM 符号 (Nfft+L*Nfft? 这里约定 y 长度为 Nfft)
% A : 限幅门限 (幅度)
% N_iter : 迭代次数
% Nfft : FFT 点数
% N_used : 有效子载波数(这里用简单中心映射示意)
% cp_len : CP 长度(不影响限幅核,但你要统一接口就留着)
x = y(:);
% --- 子载波索引:简单取中心 N_used ---
kc = floor((Nfft - N_used)/2) + 1;
idx_used = kc : (kc+N_used-1);
for it = 1:N_iter
% 1) 限幅
mag = abs(x);
phase = exp(1j*angle(x));
mask = (mag > A);
x_c = x;
x_c(mask) = A .* phase(mask); % 硬限幅(你可换软限幅)
d = x_c - x; % 限幅失真
% 2) 回频域,只保留子载波位置(滤掉带外)
D = fft(d, Nfft);
D_filter = zeros(Nfft,1);
D_filter(idx_used) = D(idx_used); % 子载波内留着
% 3) 重建时域
x = x_c - ifft(D, Nfft); % 经典写法:x_new = x_c - ifft(带外)
% 等价理解:只保留带内 => x = ifft(D_filter)
% 可选:再做一个小保护(避免数值反弹)
x(abs(x)>A) = A .* exp(1j*angle(x(abs(x)>A)));
end
x_out = x;
end
如果你要更"射频味",把频域滤波换成 FIR/矩形窗(对应 sinc 时域) 或 保留 DC/左右对称子载波 即可;结构不变。
4) 完整仿真骨架:产生 OFDM → 迭代降低 → PAPR/CCDF
4.1 主脚本
matlab
clear; clc; close all;
%% === 参数 ===
Nfft = 256;
N_used = Nfft; % 这里先不用虚拟子载波,方便看清 PAPR
M = 16; % QAM
L = 4; % 过采样倍数
N_sym = 1; % 每帧 OFDM 符号数(PAPR 通常不依赖多符号)
cp_len = Nfft/8;
% 迭代参数
N_iter = 5;
PAPR0_dB = 7; % 你想"对应的限幅门限"参考(不是保证值)
N_frame = 1000; % Monte-Carlo
%% === 归一化:令 E[|x|^2]=1 ===
% 16-QAM 平均能量 (格雷/单位平均功率归一化可自己定)
const = qammod(0:M-1, M, ...
InputType="integer", UnitAveragePower=true);
% const = (2*(randi([0 1],N,1)-0.5) + 1j*(2*(randi([0 1],N,1)-0.5))) / sqrt(2); % QPSK 例子
PAPR_vec_raw = nan(N_frame,1);
PAPR_vec_icf = nan(N_frame,1);
rng(42);
for f = 1:N_frame
%% 1) 随机数据
b = randi([0 M-1], N_used*N_sym, 1);
Xf = qammod(b, M, InputType="integer", UnitAveragePower=true);
%% 2) 映射到 FFT 位置(中心映射)
kc = floor((Nfft - N_used)/2)+1;
X = zeros(Nfft,1);
X(kc:kc+N_used-1) = Xf;
%% 3) IFFT → 过采样可在此做(最简:IFFT 后插值;更"标准"用 L*Nfft)
x_raw = ifft(X, Nfft)*sqrt(Nfft); % 归一化看你习惯;下面我们用功率归一化
% --- 功率归一化使得 E[|x|^2]=1 ---
x_raw = x_raw / sqrt(mean(abs(x_raw).^2));
% 为省事:这里先不做 L>1 插值(PAPR 仍可比,但更严格的要插值)
x = x_raw;
%% 4) 原始 PAPR
power_inst = abs(x).^2;
PAPR_raw(f) = 10*log10( max(power_inst) / mean(power_inst) );
%% 5) 限幅门限
A = sqrt(mean(power_inst)) * sqrt(10^(PAPR0_dB/10));
%% 6) ICF
x_icf = icf_ofdm(x, A, N_iter, Nfft, N_used, cp_len);
% 重新功率归一化(可选,便于公平比较)
x_icf = x_icf / sqrt(mean(abs(x_icf).^2));
power_inst2 = abs(x_icf).^2;
PAPR_icf(f) = 10*log10( max(power_inst2) / mean(power_inst2) );
end
%% === CCDF ===
PAPR_db = 0:0.1:12;
ccdf_raw = arrayfun(@(t) mean(PAPR_raw > t), PAPR_db);
ccdf_icf = arrayfun(@(t) mean(PAPR_icf > t), PAPR_db);
figure; semilogy(PAPR_db, ccdf_raw, 'k--','LineWidth',2); hold on; grid on;
semilogy(PAPR_db, ccdf_icf,'r-','LineWidth',2);
xlabel('PAPR_0 (dB)'); ylabel('CCDF = Prob(PAPR>PAPR_0)');
legend('Raw OFDM','ICF (iter='+string(N_iter)+')');
title('OFDM PAPR 降低:迭代限幅+频域滤波');
参考代码 PAPR 迭代降低算法仿真 www.youwenfan.com/contentcsv/81410.html
5) 3 个"仿真坑"
-
PAPR 看起来偏低 / 没峰
→ 你很可能没做过采样 。真 PAPR 需要用至少 (L\ge4) 的过采样(IFFT 插值或更长 IFFT)否则会漏掉峰。
做法:用
Nfft_up = L*Nfft,把频域填到更大网格再做 IFFT。 -
迭代后波形"反而更怪"
→ 常见原因:你限幅门限太低(A 太小)→ 强削波 → 带内 EVM 暴增;或者你滤波没正确"只保留子载波/带内"。先把
N_iter=1跑通、看时域波形再往上加。 -
CCDF 不稳(曲线毛毛躁躁)
→
N_frame不够。PAPR 尾部是稀有事件,通常要 几千帧 才平滑(看你精度需求)。