ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Polar编解码与PSS-SSS联合检测:MATLAB物理层仿真实现

2026/9/11 18:15:24 拓冰建站 浏览量
Polar编解码与PSS-SSS联合检测:MATLAB物理层仿真实现 简介面向5G通信研究者与通信工程学生的MATLAB仿真源码包专注于Polar编解码与PSS/SSS联合检测技术通过仿真输出同步成功率以及误比特率BER、块误码率BLER可用于评估物理层同步与信道编码性能适合深度学习5G NR同步机制和Polar码应用的读者。资源共包含68个文件以56个.m源码文件为主体另有.mat数据、.bin信道文件、头文件及mexw64插件分别用于保存信道响应、导入真实信道参数、提供C接口加速等整体压缩包仅2.63MB。目前已有146人学习下载。源码工程目录清晰涵盖系统参数、发端、信道、收端、常用函数等模块内置TU信道和DMRS相关数据可直接运行Runme.m复现仿真流程。借助该资源可快速上手Polar码编解码与联合检测的工程实现也可基于现有框架修改参数、替换信道模型是通信仿真实践和5G算法研究的高价值参考资料。1. 一个MATLAB工程把两件物理层难事放在同一条链路上“基于Polar编解码的联合检测”这个标题第一眼看像是两个独立仿真拼在一个工程里一边是Polar码的收发链路一边是PSS-SSS的小区搜索检测。实际做下来会发现标题里的“联合”不只是一个文件夹装多份脚本而是让同步检测的结果直接参与Polar译码过程——典型做法是先靠PSS-SSS盲检拿到物理小区标识和粗定时再把这些参数用于PBCH信道的解扰、解速率匹配和Polar译码最终统一用检测成功率、BER、BLER三条曲线交代整条链路的性能。这篇文章面对的读者是看过Polar码论文但没亲手搭过接收端的人以及写过LTE同步但没碰过NR同步序列的人。我不会去依赖5G Toolbox里的封装黑盒而是把从序列生成到统计误码的最小闭环一步步展开让你能在自己的数据上复现并看清每个门限和参数在结果里的作用。2. Polar编解码的最小MATLAB实现从生成矩阵到SCL译码2.1 为什么控制信道场景选Polar而不是Turbo或LDPC5G NR在物理下行共享信道用LDPC做数据信道编码在物理广播信道和控制信道用Polar这个分工本身就是工程权衡的结果。LDPC在中长码长上有接近香农限的性能但在PBCH这种只有几百比特的小净荷上码块分段带来的开销会明显吃掉编码增益延时也受迭代次数限制。Polar码的优势恰恰是短码和中等码长下表现稳定且速率匹配非常灵活——它通过打孔和缩短从母码长度适应各种调度尺寸这让它适合承载DCI和MIB这类结构不对称的小包。另一个常被忽略的点是Polar补码的构建方式明确。Arikan极化码的结构基于信道极化的递归变换信息位集合生成后编码器是一个固定矩阵的乘法复杂度O(N log N)。这个确定性使得控制信道可在严格时延预算内完成一次SCL译码而Turbo码在短码时的错误平层和LDPC在低信噪比下的高迭代代价都是实际顾虑。因此后面仿真链路里我会把Polar放在PBCH信道模型下验证而不是把它塞进高速数据业务场景。2.2 极化构造参数表与矩阵递归生成要动手写Polar编解码先要确定一组构造函数需要的参数。下表是我常用的默认配置也方便后续调参对比参数符号含义默认值调参方向N母码码长必须为2的幂512增大提升性能缩短则降低时延K信息位长度不含CRC32由MIB实际比特决定CRC位数CRC级联用于SCL路径选择8增加可降低误检但有效信息率下降冻结位集合信道容量最低的N-K个位置高斯近似生成直接影响早期译码错误传播SCL列表L译码路径数8L增大BLER下降复杂度线性上升生成矩阵是Polar编码的根基。核矩阵F为[[1,0],[1,1]]N阶生成矩阵是F的n次Kronecker幂再乘以比特反转置换矩阵。Kronecker幂可以直接用MATLAB的kron函数迭代得到但N1024时矩阵膨胀得很厉害更稳的做法是用递归结构隐式完成极化变换。下面给出一个不显式展开生成矩阵的编码思路它把输入重排后做蝶形异或操作对应的是编码的快速实现。2.2.1 极化变换的MATLAB操作function x polar_encode(u, n) % u: 长度为2^n的逻辑比特向量已含冻结位置 x u; for s 1:n % 逐级蝶形 step 2^s; idx reshape(1:length(u), step, []); % 每组的后一半与前半部分异或后更新前半段 for blk 1:size(idx, 2) i1 idx(1:step/2, blk); i2 idx(step/21:step, blk); x(i1) xor(x(i1), x(i2)); end end x bitrevorder(x); % 比特反转等效于在接收端做同样的反转 end这段代码的关键是蝶形运算的循环结构每一级把向量按2的幂拆分前半部分异或后半部分后写回级间顺序从s1到sn。这里我用bitrevorder做比特反转它和5G标准中的位置映射方式一致解码端需要在软信息送入译码器前做同样的反置换。用循环而不是矩阵乘法好处是省内存坏处是MATLAB循环在N较大时有性能损失后续优化可改用实部虚部重排的算法。2.3 SC与SCL的工程取舍列表长度的实际影响SC译码本质上是沿二叉树逐比特递归地判断似然比判定错误后会不可逆转地传播。SCL则是维护L条候选路径在每比特扩展后保留度量最高的L条直到结尾用CRC校验挑出正确路径。下面给一个框架性的SCL实现片段只截取最核心的剪枝逻辑便于解释参数含义。function [bits, path_metric] scl_decode_llr(llr, info_idx, crc_bits, L) % llr: 接收符号的对数似然比长度N % info_idx: 信息位索引冻结位在该框架中被硬置为0 paths zeros(L, numel(llr)); metrics zeros(1, L); for i 1:numel(llr) % 扩充路径每路径分裂为候选0和1 expanded zeros(2*L, i); expanded(1:2:2*L-1, :) paths(:, 1:i); expanded(1:2:2*L-1, i) 0; expanded(2:2:2*L, :) paths(:, 1:i); expanded(2:2:2*L, i) 1; % 用llr累加度量这里简化使用硬判决近似 new_metric repmat(metrics, 1, 2) expanded(:, i) .* llr(i); [~, ord] sort(new_metric, descend); paths expanded(ord(1:L), :); metrics new_metric(ord(1:L)); end % 最后用CRC做最优路径选择 for p 1:L if crc_check(paths(p, :), crc_bits) bits paths(p, info_idx); path_metric metrics(p); return; end end bits paths(1, info_idx); % 全部失败时退回度量最大路径 end以上实现做了两处简化一是路径度量直接用硬判决似然累加简化了标准SCL中的对数似然比递推二是每个比特都做全路径分裂没有按冻结位优化分支数。实际工程中应当在第i步只对信息位做扩展冻结位直接追加0且不分裂否则复杂度会翻倍。SCL的关键参数L决定性能拐点L从1增到4时BER和BLER下降最明显L超过16后增益趋饱和而每个L值的复杂度近似线性增长。提示在没有5G Toolbox的情况下CRC校验可以自己实现但需要用matlab的comm.CRCGenerator配合comm.CRCDetector验证过再接入SCL否则路径选择错误会表现为中高信噪比下BLER依旧平层。3. PSS-SSS联合检测序列生成、滑动相关与PCI盲检3.1 PSS和SSS的M序列基础5G NR的PSS和SSS都是m序列即最长线性反馈移位寄存器序列区别在于PSS基于多项式x7x41SSS基于x7x61。物理小区标识PCI由两部分组成PCI 3*NID1 NID2其中NID2取0、1、2对应PSS的三种循环移位NID1取0到335对应SSS序列的循环移位组合。联合检测的目标就是在接收信号里同时找到这两个移位量拼出完整的PCI。下面这段代码生成标准的PSS序列采用频域定义方便后续直接映射到子载波。3.1.1 PSS频域序列的MATLAB生成function pss_seq generate_pss(NID2, Nfft) % NID2: 0, 1, 2 % Nfft: Nfft主要用于占位标准长度固定为127 x zeros(1, 127); x(1:7) [0 0 0 0 0 0 1]; % 初始状态 m 1:7; for i 8:127 x(i) mod(x(i-7) x(i-4), 2); % 由多项式 x^7 x^4 1 导出 end seq 1 - 2*x; % BPSK映射 pss_seq zeros(1, 127); for n 0:126 pss_seq(n1) seq(mod(n 43*NID2, 127) 1); end % 频域直接适用做归一化以便相关 pss_seq pss_seq / sqrt(mean(abs(pss_seq).^2)); end频域PSS序列长度固定127不需要用Nfft参数生成序列本身但接收端做相关前要先做OFDM解调得到频域符号才能把收到的127个子载波和本地序列做相关。43这个移位常数来自5G标准中的循环移位定义NID2为0、1、2时分别对应移位0、43、86。归一化步骤很重要它会直接影响后续相关峰和门限的比较不归一化时功率差异会覆盖门限判断的结果。3.2 滑动相关与定时同步从粗同步到联合判决接收端最直接的做法是取同步信号所在OFDM符号的解调输出在频域对所有可能的NID2和NID1组合做相关。但在多径信道下窄带干扰或频偏可能使单个PSS相关峰不够稳定所以工程上常先用PSS做一次粗对齐确定OFDM符号边界后再联合SSS做二次判决。下面给出频域联合检测的代码输入是接收OFDM符号解调后的资源网格。function [pci, timing, strength] joint_pss_sss_detect(rx_grid, n_id2_range, n_id1_range) % rx_grid: 接收端OFDM解调后的频域资源网格大小为 Nfft x Nsym max_corr -inf; for nid2 n_id2_range pss_loc generate_pss(nid2, size(rx_grid, 1)); corr sum(conj(pss_loc) .* rx_grid(pss_indices, pss_symbol), 1); [val, sym_off] max(abs(corr)); if val max_corr max_corr val; best_nid2 nid2; timing sym_off; end end % 基于PSS找到的最佳NID2和定时位置再在SSS位置做联合判决 sss_corrs zeros(1, length(n_id1_range)); for nid1 n_id1_range sss_loc generate_sss(best_nid2, nid1, size(rx_grid, 1)); sss_corrs(nid11) sum(conj(sss_loc) .* rx_grid(sss_indices, pss_symbol 1)); end [~, best_nid1] max(abs(sss_corrs)); pci 3 * best_nid1 best_nid2; strength abs(sss_corrs(best_nid11)); end这里用两个步骤避免联合暴搜的复杂度先对NID2做三路相关取最大者确定粗定时再固定NID2后对336个SSS候选做一轮相关。总相关次数从3×337次降为3337次在MATLAB for循环下能快一个数量级。timing输出用于后续OFDM符号对齐而strength可以保存下来用于成功率判决——工程中常把strength除以噪声功率得到检测度量比直接用绝对相关值更公平。3.3 检测门限的设置不只看相关峰很多初版仿真在检测成功率上虚高原因不是算法坏而是判决标准只剩“是否找到全局最大峰”。实际系统中即使完全随机的噪声也总能找到一个最大峰来强制判决因此必须设置一个有效峰与噪声底的比例门限。常用方式是取相关结果的均值作为噪声估计期望峰值的旁瓣水平大约在均值的4到6倍之间。跳进仿真后先跑几万个纯噪声符号记录最大值分布再取PFA目标来反推门限比任何经验常数都靠谱。4. 联合仿真链路搭建从发射端到成功率、BER、BLER统计4.1 帧结构与联合检测的配合方式联合仿真需要一个完整的收发链路。发送端在每帧第一个时隙放PSS和SSS同时在同一SSB块内把MIB比特经过Polar编码、QPSK调制后映射到PBCH资源。接收端先做PSS-SSS检测获取PCI和定时随后按PCI指定的扰码和速率匹配参数解调PBCH最后执行Polar译码。整个仿真脚本结构如下外层循环遍历信噪比点中层循环遍历仿真帧数每个SNR点运行2000帧以上内层调用PSS-SSS检测函数、PBCH解调函数、Polar SCL译码函数统计量在每帧结束时更新4.2 误码统计单元BER、BLER、成功率的正确计数方法% 主仿真循环核心片段 for snr_idx 1:length(snr_db) ber_count 0; bler_count 0; block_total 0; sync_success 0; for frame 1:num_frames tx_bits randi([0 1], K, 1); [pss_sym, sss_sym, pbch_grid] tx_chain(tx_bits, PCI); rx_signal awgn_channel(pbch_grid, snr_db(snr_idx)); [est_pci, timing, strength] joint_pss_sss_detect(rx_signal); if est_pci PCI strength threshold sync_success sync_success 1; rx_bits rx_pbch_chain(rx_signal, est_pci, timing); block_total block_total 1; err_bits sum(rx_bits ~ tx_bits); ber_count ber_count err_bits; if err_bits 0 bler_count bler_count 1; end end end ber(snr_idx) ber_count / (block_total * K); bler(snr_idx) bler_count / block_total; sync_rate(snr_idx) sync_success / num_frames; end这里有一个容易混淆的统计口径BER的分母是所有译码后解码块的比特总和而不是全部发射比特总和。如果PSS-SSS检测失败那一帧的译码直接就丢了因此它的错误比特不该进BER分母。BLER同理只统计成功同步后的块集。实际做下来你会发现在低信噪比区域检测成功率下降导致block_total偏小BER曲线会出现在低信噪比下剧烈跳动的现象——这不是代码bug而是有效样本量变小导致的统计方差解决方法是加大num_frames或者至少固定随机数种子。4.2.1 统计口径速查表统计量分子分母易错点PSS-SSS检测成功率正确PCI且强度超门限的帧数总仿真帧数正确PCI但强度不足应算失败BER错误比特总数参与译码的总信息比特数不要用整帧比特数做分母BLER译码后含任意错误比特的块数成功译码尝试的总块数未同步的帧不计作译码块4.3 性能曲线绘制semilogy配合置信区间的Monte CarloMATLAB画BER和BLER曲线时数值可能跨越几个数量级线性坐标根本看不出差异所以统一用semilogy。此外很多仿真曲线在低信噪比处右端翘起来其实是同步丢失导致统计样本收缩画图前先看线性坐标下的成功率曲线能解释BER的反常上升。figure; semilogy(snr_db, ber, b-o, LineWidth, 1.5); hold on; semilogy(snr_db, bler, r-s, LineWidth, 1.5); grid on; xlabel(Eb/N0 (dB)); ylabel(Error Rate); legend(BER, BLER, Location, southwest); % 对成功率单独用plot而非semilogy yyaxis right; plot(snr_db, sync_rate, g--^); ylabel(Detection Success Rate);注意这里我用yyaxis把成功率和误码率画在同一张图的不同纵轴上成功率是0到1的量级放左轴会把曲线压缩在顶部放右轴更合理。x轴标签用Eb/N0而非SNR因为Polar译码后的性能对比要考虑速率匹配和CRC开销统一为每信息比特的噪声功率密度才可复现。5. 验证仿真正确性的三个关键技巧5.1 用纯噪声标定相关门限而不是拍脑袋定阈值联合检测成功率曲线对门限非常敏感门限高了低信噪比下检测不到门限低了高信噪比下误检。标准做法是先把PSS-SSS发射机关闭让接收端只收到复高斯噪声重复执行数千次检测记录相关度量的分布选取满足目标虚警概率的那一点。这个经验值可以作为仿真中threshold的初始值后续再微调。我在实际项目中通常用噪声场景下第99.9百分位的度量值作为门限效果比固定乘噪声均值更稳定。5.2 验证SCL译码的正确性固定L1先对比SCSCL译码比SC复杂出问题时往往是路径度量计算或CRC选择逻辑有bug。一个稳妥的验证路径是先把L固定为1让它退化为SC译码然后对照一个从零实现的SC译码器或对比论文中的性能曲线。两者输出完全一致后再把L提升到4、8观察BLER是否单调下降。如果L2比L1差基本可以断定路径度量或剪枝算法存在符号错误。此外要在CRC级联前先只跑无CRC的场景确认Polar译码本身没错再加CRC做终选。5.3 固定随机种子复现高信噪比区域的“错误平层”Polar码在高信噪比下的错误平层通常由两个因素导致冻结位选择不佳或SCL路径数不足。若BER曲线在中高信噪比出现拐点不再下降先检查冻结位集合是否用了巴氏参数而非随机选取。MATLAB的bhattacharyya函数可以计算每个比特信道的容量按容量从大到小排序后把最小的一部分做冻结位。随机冻结位在高信噪比下会残留固定数量的错误比特表现就是BER曲线平台出现这种现象时不要急着加信噪比点数先去换冻结位生成方案再考虑增大L。最后还可以跑一组不同随机种子观察曲线波动幅度是否在几个dB内如果相同信噪比下BER差异超过一个数量级说明仿真帧数不足BLER置信区间过大。本文还有配套的精品资源点击获取