做物理层仿真的朋友大概都经历过这个阶段论文里把 FBMC 的带外泄漏夸得天花乱坠把 UFMC 说成短包场景的最优解可一旦自己动手写仿真跑出来的误码率曲线要么纹丝不动要么跟文献差了好几个 dB。我在几年前接手一个多载波波形的对比评估任务时就掉进过这个坑——花了三周时间最后发现是自己把滤波器增益归一化搞错了白白怀疑了半个月的信道模型。这套“5G 多载波仿真包”最初只是我一个人用的脚本集合后来被同组几个人反复借用才慢慢整理成模块化的结构覆盖 OFDM、FBMC、UFMC 三种波形的生成、信道注入、接收解调和指标统计。这篇文章想讲清楚的不是三种波形的教科书定义而是当你真的要把它们放进同一个仿真框架里跑对比时需要注意什么。参数怎么对齐才算公平滤波器怎么设计才不掉进归一化的陷阱误码平台到底是算法问题还是实现问题这些都是文档里不会写、但会让你多熬几个通宵的东西。适合已经了解 OFDM 基础、正在做波形评估或者准备复现某篇对比论文的读者也适合想把这套代码当教学素材用的同学。下面按“原理取舍—框架搭建—链路实现—测量口径—踩坑排查”的顺序展开每一步都附上我实际用过的参数和代码骨架。1. 三种波形摆在一张桌子上比较之前先想清楚各自的代价很多人做对比的第一反应是“把三种波形都实现一遍然后画 BER 曲线”这个思路本身没错但容易忽略一件事这三种波形并不是在同一维度上竞争的方案它们各自牺牲了不同的东西。如果一开始没想明白牺牲的是什么后面的参数对齐就会变成拍脑袋。我在第一章先把这三笔“交易”讲透后面搭框架的时候你才知道哪些参数必须锁死、哪些可以放开。1.1 CP-OFDM 的矩形脉冲决定了它的带外泄漏OFDM 的本质极其简单把频域符号做一次 IFFT 搬到时域每个子载波在时域上就是一个持续 T 的矩形窗。矩形窗的频谱是 sinc 函数第一旁瓣只比主瓣低大概 13 dB而且滚降极慢按频率的平方衰减。这意味着什么假设你有 100 个资源块其中只有 60 个被占用剩下的空着当保护带那些空资源块上依然会收到邻近占用子载波漏过来的能量泄漏强度大概在 -20 到 -30 dBc 这个量级。在连续频谱、大带宽的场景下这不算大问题反正整块频谱都归你用。可一旦遇到非连续频谱聚合、频谱碎片化、或者跟别的系统邻频共存这个泄漏就变成了实打实的干扰源。业界对付这个问题的常规手段是加保护带、加时域窗函数比如 raised-cosine 边缘窗、或者做滤波 OFDM。前两种手段治标第三种手段改结构。但注意CP-OFDM 有一个别人比不了的优点循环前缀把多径信道变成了循环卷积接收端一个一抽头频域均衡就能搞定实现复杂度低到可以在手机上用几百毫瓦跑完。5G NR 的下行最终选择 CP-OFDM核心考量就是这个——复杂度换灵活性这笔账在基站侧和终端侧都算得过来。所以你在仿真里比较 OFDM 的时候要明确一件事你比较的是“裸的 CP-OFDM”还是“加了窗的 CP-OFDM”这两者的带外泄漏能差 10 dB 以上如果文献里没写清楚你复现出来的曲线对不上太正常了。我在自己的包里统一采用裸 CP-OFDM 作为基线加窗版本单独作为一个配置项标签写清楚免得日后自己也搞混。1.2 FBMC 拿时域拖尾换频谱锐度FBMC 的思路是彻底放弃“矩形脉冲”这个前提。它给每个子载波单独套一个原型滤波器滤波器的时域长度是符号周期的 K 倍K 叫重叠因子常用 4频域上则是设计得很陡的过渡带。这样一来每个子载波的频谱几乎被限制在自己的带宽内带外泄漏能压到 -60 dBc 甚至更低相邻子载波之间的保护带可以做得极窄理论上频谱效率比 CP-OFDM 高 10% 到 30%。代价在哪第一滤波器时域拖了 K 个符号长相邻符号在时域上强烈重叠你必须用多相网络polyphase network才能高效实现运算量比一次 IFFT 大不少。第二也是最要命的一点滤波器组在满足完美重建条件时相邻时频格点之间存在固有虚部干扰intrinsic imaginary interference。简单说你想在某个时频点放一个 QAM 符号但相邻格点漏过来的能量会叠加到你身上而且这个叠加量在理想条件下是纯虚数会污染你的实部判决。FBMC 用 OQAM交错正交幅度调制来应对把复数 QAM 符号拆成实部和虚部分别放在时间上错开 T/2、频率上错开 1/(2T) 的格点上让实部只受虚部干扰、虚部只受实部干扰接收端取实部或虚部就能避开。这套机制在单天线、理想同步条件下工作得很好。可一旦引入多径信道、载波频偏、或者 MIMO那个“纯虚数”的性质就被破坏了你必须引入导频序列IAM、IAM-C、 auxiliary pilot 等方案来估计并消除干扰导频开销上去了频谱效率的优势又被吃掉一部分。这也是 FBMC 在 5G 标准化过程中最终没能成为主流方案的重要原因之一不是它不好而是它的复杂度代价在宽带 MIMO 场景下被放大了。1.3 UFMC 把滤波器挪到子带这一级UFMC 走的是中间路线。它不滤波每个子载波那样太贵也不完全不滤那样泄漏大而是把子载波分成若干个子带每个子带内保持 OFDM 的矩形脉冲结构只在子带边缘用一段长度为 L 的 FIR 滤波器做整形。子带内的子载波之间依然是正交的不需要 OQAM 那套虚部干扰处理子带之间被滤波器隔开带外泄漏比纯 OFDM 低十几到二十几个 dB。这个结构的巧妙之处在于滤波器长度 L 通常远小于 FFT 点数 N典型配置下 L 在 64 到 80 之间N 是 1024所以滤波的运算量是 O(B×L×N_sub)B 是子带数量比 FBMC 的多相网络轻。而且因为子带内还是 OFDM它天然兼容 MIMO 和现有的信道估计方案接收端改动很小。代价是带外泄漏压得不如 FBMC 彻底毕竟滤波器阶数有限而且子带边缘的几个子载波会落在滤波器过渡带里幅度和相位被压得不平如果不留保护子载波边缘子载波的误码率会明显高于子带中心。另外滤波器会引入额外的时域拖尾需要在子带滤波之后补一段 CP 或者零前缀CP 长度的选择要兼顾多径时延和滤波器拖尾两件事。1.4 一张表看清三者的取舍把上面这些摊开我习惯用这样一张表来提醒自己每个方案的“软肋”在哪维度CP-OFDMFBMC (OQAM)UFMC带外泄漏相对值高约 -20~-30 dBc很低可到 -60 dBc 以下中等约 -40~-50 dBc是否需要 CP需要约 7% 开销不需要通常需要可缩短时域拖尾长度短仅 CP长K 倍符号周期中等滤波器长度 L多发多收兼容性好差需专门设计好改动小接收端均衡一抽头频域均衡需处理固有干扰一抽头频域均衡为主计算复杂度相对1.02.0~3.01.3~1.8典型适用场景连续大带宽、eMBB碎片频谱、认知无线电短包、低时延、异步接入这张表不是让你背的是让你在写仿真配置的时候对照着检查我这次比较的是不是同一个维度比如你想证明 FBMC 频谱效率高那就要在相同带宽、相同误码率下比较有效吞吐而不是在相同 FFT 点数下比较因为 FBMC 不需要 CP符号速率天然比 OFDM 高。2. 仿真包的骨架让波形、信道、接收机各自独立可换写完第一版脚本之后我最大的痛点是三种波形的代码互相纠缠改一个参数要动三个文件跑完一组数据想换个信道得手动改十几处。这个包重构了两次最终定下来的思路是“三段式”——发射机、信道、接收机各自是独立函数中间用结构体传递信号和元数据。这样换波形只需要换发射机和接收机信道和指标统计完全复用。2.1 目录与模块边界我实际用的目录结构大致是这样你可以按自己的习惯调整命名但边界的划分建议保持一致waveform_sim/ ├── config/ │ ├── cfg_ofdm.m │ ├── cfg_fbmc.m │ └── cfg_ufmc.m ├── tx/ │ ├── tx_ofdm.m │ ├── tx_fbmc.m │ └── tx_ufmc.m ├── channel/ │ ├── ch_awgn.m │ ├── ch_multipath.m │ └── ch_impairment.m % 频偏、相噪、定时误差 ├── rx/ │ ├── rx_ofdm.m │ ├── rx_fbmc.m │ └── rx_ufmc.m ├── metrics/ │ ├── calc_ber.m │ ├── calc_oob.m % 带外泄漏 │ ├── calc_papr.m │ └── calc_complexity.m └── run_compare.m % 顶层脚本串起全流程关键约定有三条。第一配置结构体是唯一入口。每个 cfg 文件返回一个 struct里面包含采样率、FFT 点数、子载波间隔、调制阶数、滤波器参数、信道类型、SNR 扫描范围所有函数只从这个 struct 里读参数绝不硬编码。第二发射机输出统一格式[waveform, meta]waveform 是列向量基带信号meta 里放采样率、符号数、有效子载波索引这些信息。第三接收机只依赖 meta不依赖具体波形类型这样信道模块完全不用改。这套约定的好处在调试时特别明显。有一次我怀疑是信道模型的多径时延设置有问题直接把三种波形的发射信号分别灌进同一个ch_multipath跑出来的结果一对比立刻确认问题不在信道而在 FBMC 的接收端——因为 OFDM 和 UFMC 在同样信道下曲线是正常的。2.2 一套参数贯穿三种波形采样率、子载波间隔、FFT 点数参数对齐是这套包最容易出问题的地方我把自己的取值列出来供参考。基础配置是子载波间隔 15 kHzFFT 点数 N 1024采样率 fs N × 15 kHz 15.36 MHz占用子载波数 600对应约 9 MHz 占用带宽CP 长度 72 个采样点。为什么是 7272 / 15.36 MHz 约等于 4.69 微秒能覆盖大约 1.4 km 的时延扩展对应典型城市宏小区的多径环境。同时 72/1024 ≈ 7.03%跟 LTE 的常规 CP 开销基本吻合做对比时不会因为 CP 开销差异太大而失真。如果你要评估短包场景可以把 CP 压到 36 甚至更短但记得在论文表格里注明。FBMC 这一侧参数要特别处理。它没有 CP符号周期就是 1/15 kHz ≈ 66.67 微秒但滤波器重叠因子 K 4意味着每个符号在时域上占据 4 个符号周期的长度。采样率保持一致仍为 15.36 MHz每次 IFFT 依然是 1024 点但输出信号的样点数会是输入符号数的 K 倍左右考虑重叠相加的边界。做 BER 对比时必须按“每个子载波承载的比特数 / 占用带宽”来折算频谱效率不能简单地用样点数去除。UFMC 的配置要额外确定两个量子带数量和滤波器长度。我一般把 600 个子载波均分成 12 个子带每个子带 50 个子载波子带之间留 2 个子载波做保护。滤波器长度 L 取 74用 Dolph-Chebyshev 窗设计旁瓣衰减设 40 dB。这个组合是我试了几轮之后觉得平衡点比较好的L 再大带外泄漏改善有限但拖尾变长L 再小子带边缘的子载波被压得太厉害。2.3 功率归一化最容易埋雷的地方这一节我要单独拎出来讲因为它毁掉了我整整两周的工作。三种波形在发射端做完 IFFT 和滤波之后输出信号的功率是完全不一样的如果不做归一化就送进 AWGN 信道你比较的其实是“不同发射功率下的误码率”曲线毫无意义。正确的做法是在发射机输出端强制归一化到单位平均功率具体就是在返回 waveform 之前除以sqrt(mean(abs(waveform).^2))。听起来很简单对吧坑在于滤波器那一步。FIR 滤波会改变信号功率改变量取决于滤波器系数的能量。我最初的 FBMC 实现里原型滤波器系数直接从频域系数做 IFFT 得到没有做能量归一化结果滤波器输出的信号功率比输入高了将近 4 dB。这意味着我的 FBMC 曲线看上去“比 OFDM 好 4 dB”实际上是偷偷多发射了 4 dB 功率。等发现的时候基于这组数据写的半篇分析全废了。注意滤波器设计完之后务必检查sum(abs(h).^2)是否等于 1或者在滤波之后单独做一次功率归一化。两种做法都行但要在代码注释里写清楚用的是哪一种否则后面接手的人包括三个月后的你自己会疯。UFMC 的归一化还多一层每个子带的滤波器系数能量可能不同如果各子带滤波器不一致子带之间的功率会失衡接收端做信道估计时会出现系统性的幅度偏差。我的处理办法是所有子带共用同一个滤波器系数只做频移这样能量天然一致。3. 逐条链路的实现从比特到基带波形框架搭好之后剩下的是把三条链路填进去。这一章给出我实际在用的核心代码骨架重点是那些“看起来应该这样写、实际上必须那样写”的细节。3.1 CP-OFDM 的最小实现与 CP 长度选择OFDM 的发射端短得可以贴在便利贴上但有两处容易写错。第一处是 IFFT 之后的功率缩放MATLAB 的ifft默认除以 N而通信仿真里习惯让时域和频域功率保持一致所以要乘回sqrt(N)。第二处是 CP 的拼接顺序取的是时域符号的尾部放到头部。function [wave, meta] tx_ofdm(cfg, bits) N cfg.N; % 1024 Ncp cfg.Ncp; % 72 Nsc cfg.Nsc; % 600 占用子载波 idx cfg.subcarrierIdx; % 占用子载波的索引向量 % 1. 比特映射 sym qammod(bits, cfg.modOrder, UnitAveragePower, true); % 2. 符号分块 子载波映射 nSym numel(sym) / Nsc; symM reshape(sym, Nsc, nSym); grid zeros(N, nSym); grid(idx, :) symM; % 3. IFFT 并补 CP x ifft(grid, N, 1) * sqrt(N); xc [x(end-Ncp1:end, :); x]; wave xc(:); % 4. 功率归一化 wave wave / sqrt(mean(abs(wave).^2)); meta struct(fs, cfg.fs, N, N, Ncp, Ncp, ... nSym, nSym, subcarrierIdx, idx); endCP 长度的选择我在 2.2 节已经说过取值这里补充一个经验做波形对比时 CP 长度要固定不要为了让某个波形好看而单独调。我见过有人给 OFDM 配 72 点 CP给 UFMC 配 36 点然后论证 UFMC 频谱效率高——这个结论当然成立但它证明的是“短 CP 比长 CP 效率高”跟波形本身没关系。要比较波形CP 开销要么完全一致要么在结论里明确折算。3.2 FBMC 的 OQAM 调制与 PHYDYAS 原型滤波器FBMC 是三者里实现难度最高的难点集中在两处OQAM 的实虚交错排列以及多相网络的实现方式。先说滤波器。我用的原型滤波器来自经典的频域系数设计重叠因子 K 4 时频域系数取[1, 0.97196, 0.70711, 0.23515]这四个数决定了滤波器的频率响应形状把它们铺成一个长度为 K×N 的频域序列再做 IFFT就得到时域滤波器function h design_phydyas(K, N) Hfreq [1, 0.97195983, sqrt(2)/2, 0.23514695]; H zeros(K, N); for k 1:K H(k, :) Hfreq(k); end h ifft(H(:), K*N); h h / sqrt(sum(abs(h).^2)); % 能量归一化关键 endOQAM 的排列规则记住一句话就行复数符号拆成实部和虚部实部放在偶数时间格点、虚部放在奇数时间格点同时频率方向每隔一个子载波错开半格。具体实现上我先把 QAM 符号取实部再做一次“时间偏移半个符号”的插值然后跟虚部数据交错拼成两倍长度的序列最后做多相滤波。% 简化版 OQAM 映射单帧 a real(sym); b imag(sym); seq zeros(2*nSym, Nsc); seq(1:2:end, :) a; % 实部偶数时刻 seq(2:2:end, :) b; % 虚部奇数时刻 % 频率方向再做 exp(j*pi*n/Nsc) 的相位旋转 n (0:Nsc-1).; rot exp(1j * pi * n / 2); seq seq .* repmat(rot., 2*nSym, 1);多相滤波那一步教科书上给的是分析滤波器组和综合滤波器组的完整推导但在仿真里我采用的是更直接的重叠相加法每个 OQAM 符号做一次 N 点 IFFT把结果扩展成长度 K×N 的帧乘以滤波器系数后按 N/2 的步长叠加到输出缓冲区。这个实现运算量大一些但逻辑清晰、容易调试对仿真来说完全够用。真要追求速度再改成多相结构。这里有一个必须提醒的地方OQAM 的实虚交错意味着每个子载波上的符号速率翻倍了所以做 BER 统计时比特映射的对应关系要格外小心。我最初统计出来的 BER 一直停在 0.5查了两天才发现是接收端解调出来的实虚部跟发送端的顺序错位了半个符号。3.3 UFMC 的子带划分与 Dolph-Chebyshev 滤波器UFMC 的实现相对直白流程是比特映射 → 子带分组 → 每子带补零到 N 点做 IFFT → 每个子带乘以频移后的滤波器 → 所有子带叠加 → 补 CP。滤波器用 Dolph-Chebyshev 窗设计好处是能在给定旁瓣衰减下得到最窄的主瓣function [wave, meta] tx_ufmc(cfg, bits) N cfg.N; Ncp cfg.Ncp; L cfg.L; % 74 B cfg.numSubbands; % 12 NscB cfg.NscPerSubband;% 50 idxB cfg.subbandIdx; % B x NscB 矩阵 sym qammod(bits, cfg.modOrder, UnitAveragePower, true); nSym numel(sym) / (B * NscB); symM reshape(sym, B, NscB, nSym); % 每个子带的滤波器频域平移 fBase fir1(L-1, 1/(B*2), chebwin(L, 40)); fBase fBase / sqrt(sum(abs(fBase).^2)); wave zeros((N Ncp) * nSym, 1); for s 1:nSym frame zeros(N L - 1, 1); for b 1:B g zeros(N, 1); g(idxB(b, :)) symM(b, :, s).; xb ifft(g, N) * sqrt(N); fb fBase .* exp(1j * 2 * pi * (b-1) * (0:L-1). / (B*2)); frame frame conv(xb, fb); end frame frame / sqrt(mean(abs(frame).^2)); % 截取有效部分并补 CP seg frame(1:NNcp); seg(1:Ncp) frame(N-L2:N-L1Ncp); wave((s-1)*(NNcp)1 : s*(NNcp)) seg; end ... end这段代码里有两个细节值得展开。第一个是子带滤波器要做频移。基带滤波器设计出来是低通的直接卷到每个子带上会破坏子带结构必须乘上exp(j*2*pi*f_b*t)把它搬到对应子带的中心频率。这个频移量我按“子带序号 × 子带带宽”来算子带带宽是1/(B*2)的归一化频率具体推导涉及采样率换算写的时候建议直接用频域平移更不容易错。第二个是 CP 的取法。因为滤波器带来了 L-1 个采样点的拖尾CP 不能简单地从 IFFT 输出末尾取而要从滤波后信号的有效尾部取。我一开始就是这里搞错了导致 UFMC 的误码率曲线在低 SNR 下跟 OFDM 几乎重合但在高 SNR 下突然翘起来查了很久才定位到 CP 拼接位置。另外子带之间要不要留保护子载波这个取决于滤波器过渡带的宽度。我这里每个子带 50 个子载波、留 2 个保护实际测试下来边缘子载波的 SNR 损失大约 1.5 dB可以接受。如果你把滤波器旁瓣压到 60 dB过渡带会变宽保护子载波要相应加到 4 个以上。3.4 三种波形共用的信道与损伤注入模块信道模块我做了三层纯 AWGN、多径EPA/EVA/ETU 三种标准时延剖面、以及可叠加的损伤载波频偏、采样钟偏、相位噪声、定时偏移。这样设计的原因是波形的差异往往在小损伤下才暴露出来。在理想 AWGN 下三种波形的 BER 曲线几乎完全重合你什么都看不出来一旦加上 100 Hz 的载波频偏或者 -80 dBc/Hz 的相噪FBMC 的固有干扰处理不当的问题立刻现形。损伤注入的顺序要注意频偏应该在多径信道之前施加模拟发射机与接收机晶振不同步定时偏移应该在接收端做 FFT之前施加。我试过把频偏放在信道之后结果发现 OFDM 的抗频偏能力“变好”了其实是频偏被信道卷积的相位旋转部分抵消了一部分这个顺序错了会导致整个对比结论失真。4. 性能对比的测量口径怎么比才不算耍赖代码跑通只是第一步真正决定这套包价值的是测量方法。同一组仿真数据换个统计口径结论可能完全反转。这一章讲我在做对比时固定下来的几条规则。4.1 BER 曲线的对齐条件画 BER 曲线之前必须问自己四个问题发射功率一样吗频谱效率一样吗带宽一样吗CP 开销算进去了吗这四个问题没有统一答案但必须在图注里写清楚。我采用的默认口径是“相同发射功率、相同占用带宽、相同信息速率”。前两条好办第三条需要折算FBMC 没有 CP同样的符号周期下它能多传 7% 的数据要让它跟 OFDM 信息速率一致就得把 FBMC 的符号周期拉长 7%等于人为降低它的优势。反过来如果按相同符号周期比较FBMC 的频谱效率优势就体现出来了。我的做法是同时给出两组曲线一组按相同符号周期用来展示波形的原生频谱效率另一组按相同信息速率用来展示实际吞吐。两组曲线放在一张图的两个子图里读者自己判断。这种画法在评审的时候被夸过说“看得出是真跑过仿真的”。具体的 SNR 扫描范围我一般从 -5 dB 扫到 25 dB步长 1 dB每个点至少 200 帧总的错误比特数控制在 1000 以上才认为曲线可信。低 SNR 段由于错误太多统计快高 SNR 段要加大帧数否则曲线尾部抖动得厉害。4.2 带外泄漏与频谱效率的量化带外泄漏的测量有两个口径绝对功率谱密度和相对泄漏比。我推荐用后者即计算“带外指定频段内的总功率 / 带内总功率”单位 dBc。具体做法是对发射信号做长点数 FFT至少 8192 点加 Hann 窗减少截断效应取带外区间的功率谱积分。这里有个坑加窗会改变旁瓣结构。如果你在计算泄漏时用了 Hann 窗测出来的泄漏会比实际低 10 dB 以上——因为 Hann 窗本身的旁瓣就低。所以测量带外泄漏时要么用矩形窗但要做长 FFT 补偿要么用 Kaiser 窗并把窗的影响在报告里说明。我统一用矩形窗加 16384 点 FFT虽然频谱看起来毛刺多但数值可信。频谱效率的折算公式我用的是SE (有效子载波数 × 每子载波比特数 × (1 - 导频开销)) / (占用带宽 × 符号周期)。FBMC 的导频开销跟 OFDM 不同前者需要更密的导频来估计固有干扰这个必须在公式里体现否则就是耍赖。4.3 PAPR 的 CCDF 统计口径PAPR 是三种波形差异比较明显的一个指标。OFDM 的 PAPR 在 CCDF 10⁻³ 时通常在 10 到 11 dB 之间FBMC 因为多载波叠加的形式不同PAPR 略高一点UFMC 因为子带滤波削掉了一部分峰值反而略低于 OFDM。统计时要注意三点。第一采样率要过采样。按 Nyquist 采样的离散信号会低估峰均比我一般做 4 倍过采样再算 PAPR这样跟理论值差距在 0.3 dB 以内。第二要先做功率归一化再算否则高功率波形的 PAPR 反而更低逻辑上就错了。第三统计样本量要够CCDF 到 10⁻³ 意味着至少要 10⁴ 个符号我一般跑 5×10⁴ 个。function papr_dB calc_papr(wave, os) w resample(wave, os, 1); % 4倍过采样 p abs(w).^2; p p / mean(p); % 功率归一化 papr_dB 10 * log10(max(p)); end4.4 复杂度用什么单位衡量比较运算复杂度时文献里用的单位五花八门复数乘法次数、实数乘法次数、逻辑门数、甚至是“相对复杂度”。我统一用每调制符号的复数乘法次数作为基准原因是它跟实际实现的相关性最强而且跨波形可比。按这个口径OFDM 的发送端复杂度约是(N/2)·log₂N N主要来自 IFFT 和子载波映射。FBMC 因为要做 K 倍重叠的多相滤波复杂度约是 OFDM 的 2.5 倍。UFMC 的滤波部分按B × L × NscB估算加上一次 N 点 IFFT整体大约是 OFDM 的 1.5 倍。这个数字你一定要在自己的代码上实测一遍不要照抄文献。因为实现方式差异很大——用重叠相加法实现 FBMC比我上面给的公式贵不少用多相网络则便宜。我在报告里会注明“基于本项目所用实现”避免误导。5. 跑仿真时真正卡住我的几个问题前面讲的都是“应该怎么做”这一章讲“做的时候会出什么事”。这些问题的排查过程我都完整保留了你可以按图索骥。5.1 BER 曲线卡在 0.5 不动符号对齐错误第一次跑 FBMCBER 曲线从头到尾一条水平线正好在 0.5。这个数值意味着接收端完全在瞎猜输出跟输入无关。我的排查链路是这样的先检查星座点发现接收端的星座是散开的四团说明信号到了但相位乱了。接着把信道换成纯 AWGN 且 SNR 设成 30 dB星座还是散的说明不是噪声问题。然后跳过信道直接把发射信号喂给接收机星座还是散的——问题一定在发射或接收的映射关系上。最后定位到 OQAM 的实虚交错位置。发送端我把虚部放在奇数索引接收端却按偶数索引取错了一位导致每一个符号都取到了另一个符号的分量。修复方式很土在发送端的 meta 里显式记录交错规则接收端只按 meta 取不再各自约定。提示跨模块传递的“隐式约定”是仿真代码的头号杀手。凡是收发两端都要用到的规则交错方式、子载波索引、导频位置一律写进 meta宁可结构体大一点。5.2 FBMC 误码平台虚部干扰没有处理修好对齐问题之后FBMC 的曲线在 BER 10⁻³ 附近出现了一个平台SNR 再加 10 dB 也降不下去。这个现象很典型说明存在一个与 SNR 无关的固定干扰源。原因是我在接收端只用了一抽头频域均衡把每个子载波上的接收符号直接除以信道估计值。但 FBMC 的 OQAM 符号在均衡之后原本“纯虚数”的固有干扰被信道的复系数一乘实部和虚部混在一起了一抽头均衡无法消除。解决方式有两种。轻量方案是用 IAM-C 导频在导频符号的左右相邻位置放三个非零符号接收端利用这些已知符号估计出干扰项的复系数再做消除。重量方案是用完整的干扰消除接收机代价是复杂度翻倍。我在仿真包里两种都实现了默认走轻量方案因为它在中等 SNR 下能把平台压到 10⁻⁴ 以下够用了。顺带说一句这个现象也解释了为什么 FBMC 在实际系统里推广困难平台效应在理想仿真里还能靠导频压下去到了真实信道、高速移动下干扰系数变化太快导频密度得加上去开销就吃掉优势了。5.3 UFMC 子带边缘子载波误码偏高UFMC 的问题比较隐蔽平均 BER 曲线看着正常但把每个子载波的 BER 单独画出来会发现子带最边上两个子载波的误码率比中间高出一个量级。原因是滤波器过渡带把边缘子载波的幅度压低了等效 SNR 下降。我试过三个方案。第一个是加大保护子载波数量从 2 个加到 4 个改善明显但频谱效率掉了 3%。第二个是用更陡的滤波器旁瓣 60 dB过渡带变宽但阻带更深实际效果跟第一个差不多。第三个方案最有意思做频域均衡时把滤波器的频率响应也补偿进去相当于把子带边缘的衰落反卷积掉。这个方案在低 SNR 下会放大噪声但在中高 SNR 下效果最好误码率基本跟子带中心持平。最终我在包里把这三个方案做成可选项默认用第三个并且只在 SNR 10 dB 时启用避免低 SNR 下的噪声放大。5.4 仿真时间从几小时压到几十分钟三种波形、四种信道、五个 SNR 点、每个点 200 帧最初的脚本跑完要六个多小时。压时间的办法按收益排序如下。第一把内层循环向量化。UFMC 里那个按符号遍历的for s 1:nSym循环直接改成三维矩阵运算速度提升将近 8 倍。FBMC 的重叠相加部分也做了类似处理用一个reshape加accumarray替代循环。第二缓存重复计算。滤波器系数、子载波索引、QAM 星座图这些跟 SNR 无关的东西全部在循环外算一次。第三并行化 SNR 扫描。parfor一行代码解决跑在 8 核工作站上直接快 6 倍多。需要注意的是信道随机种子要按 SNR 点独立设置否则并行之后各点的噪声实现不可复现。第四把误码统计提前终止。当某个 SNR 点的错误比特数已经超过 2000 时继续跑下去对曲线精度贡献很小直接跳出。这一条在低 SNR 段收益巨大。做完这四步同样的仿真量大概 40 分钟能跑完迭代速度完全不一样了。6. 把这套包用到实际评估里的几点体会代码整理完之后这套包被我用来做过几次不同目的的评估场景一变选型的结论也会变。这里说几个跟常见认知不太一样的地方。6.1 什么时候真的该选 FBMC从仿真结果看FBMC 的优势只在一种情况下特别明显可用频谱极度碎片化而且邻频有强干扰源。我做过一组对比把 600 个子载波拆成三段不连续的 200段间留 30 个子载波的空隙结果 OFDM 因为泄漏空隙里残留的能量让有效 SINR 掉了将近 6 dB而 FBMC 几乎不受影响。这种场景下 FBMC 的频谱效率优势能到 25% 以上完全值得那个复杂度。但如果频谱是连续的、带宽又大FBMC 就很不划算。它省下来的 CP 开销大约是 7%可导频开销要多花 5% 到 8%加上实现复杂度翻倍账算不过来。这也是为什么大规模商用的宽带系统最终没有选它。6.2 短包场景下 UFMC 的吸引力UFMC 最被低估的一点是它对定时同步的宽容度。短包传输里每个包只有几十个符号同步误差没法靠长训练序列慢慢收敛。我做过一组测试人为引入 ±3 个采样的定时偏移OFDM 的 BER 恶化了将近一个数量级UFMC 只恶化了 2 dB 左右。原因是子带滤波本身对带外能量有抑制作用定时偏差带来的符号间干扰被滤波器截掉了一部分。这个特性在机器类通信、工业控制这类“小包 低时延 异步接入”的场景里价值很高。当然代价是带外泄漏比 FBMC 高如果邻频有严格共存要求还得掂量。6.3 代码复用与结果可复现最后说一个工程上的事。这套包我后来交给别人接手对方跑出来的结果跟我差了一个 dB查了半天发现是 MATLAB 版本不同导致的qammod归一化方式变化。从那以后我在包里加了一个reproduce_check.m每次改动之后跑一遍输出一组固定的 BER 数值跟基线对比。数值对不上就说明有什么东西变了。这个习惯救了我好几次。仿真代码最大的问题不是写不对而是“不知道什么时候悄悄变了”。参数、随机种子、函数默认行为、并行顺序任何一个动一下曲线就可能不一样。加一个回归检查脚本成本不到半小时省下的时间远不止这些。实际用下来我觉得这套包最有价值的地方不是三种波形的实现本身而是它把“公平比较”这件事变成了代码里的硬约束——参数从同一个配置结构体出功率在发射端强制归一指标统计用同一套函数。这样就很难在无意中做出对自己想证明的结论有利的偏差。至于三种波形到底谁更好我的答案是看你的频谱长什么样、你的终端算力有多少、你的业务是长包还是短包没有通用答案只有具体条件下的权衡。