简介:这是基于MATLAB的Polar编解码联合检测仿真源码,面向5G通信、无线信号处理领域的研究者与进阶学习者。整套方案聚焦PSS/SSS联合检测成功率以及BER、BLER性能指标输出,可用于同步算法验证、信道估计和编码性能评估。源码共68个文件,以56个.m脚本为核心,涵盖信号生成、Polar编码解码、联合检测策略、信道模型及性能计算模块;另含.mat数据文件、.bin信道样本、说明txt与mexw64辅助运行文件,打包后约2.63MB。目前已有146人学习。通过阅读和运行源码,可直观掌握Polar码在系统同步中的实际应用,理解PSS/SSS联合检测的流程与参数配置,并借助现成的BER/BLER计算模块完成自定义场景仿真,对未来5G协议研究和算法设计具有较高参考价值。
1. 联合检测不是串行流程:Polar译码器如何反哺PSS-SSS同步估计
在LTE/NR物理层仿真里,PSS-SSS同步检测和Polar信道译码通常被当作两个独立的模块:先做时频同步,再做下行控制信道(如PBCH)的Polar译码。但拿到这套MATLAB工程后会发现,它的核心设计思路是把两者揉进同一条链路——PSS/SSS联合检测得到的信道估计结果不只是在同步阶段用一次,而是作为Polar译码器计算LLR(对数似然比)时的先验信息,同时译码输出的CRC校验结果又反向决定是否需要重新调整同步参数。这种闭环结构对仿真平台的复用性要求很高,工程里通过Runme.m统一调度发端、信道、收端三个目录下的函数,用全局变量结构体(比如system_param)在模块间传递软信息,而不是各自为政地重复估计信道。
这个工程适合两类人:一是做NR物理层算法验证的工程师,需要对照PSS-SSS检测成功率、BER、BLER三类指标来评估同步误差对Polar译码性能的影响;二是准备5G相关课程设计的学生,源码里cellsearch.mat、TU_channel_An11_I.bin这类文件直接对应3GPP TR 38.900定义的TDL信道模型,可以省去自己搭链路的大半工作量。需要提醒的是,项目默认的采样率和子载波间隔是按30 kHz子载波间隔、1536点FFT配置的,如果直接跑Runme.m拿到的是固定参数下的仿真结果,想换带宽或 numerology 要同时改系统参数.m里的FFTSize和PSS生成函数里的ZC序列根索引。
2. Polar编解码的软信息表示:从信道极化到LLR更新
2.1 信道极化与编码矩阵的生成逻辑
Polar编码的理论基础是信道极化——把N个独立信道通过递归的蝶形结构合并又分裂,使一部分信道容量趋近于1(信息位),另一部分趋近于0(冻结位)。在MATLAB工程里,对应的是Polar编码目录下的PolarEncoder.m,它并不直接调用comm.PolarEncoder系统对象,而是按Arikan论文的G_N矩阵递推公式手动实现,核心代码段是这样的:
function [encoded, info_bits] = polar_encode(data, K, N, frozen_set) % N: 码长,必须是2的幂;K: 信息位长度;frozen_set: 冻结位索引 G = 1; for stage = 1:log2(N) G = [G 0; G G]; % Kronecker 幂构造生成矩阵 end u = zeros(1, N); u(info_bits) = data; % 信息比特放入信息位 u(frozen_set) = 0; % 冻结位置零 encoded = mod(u * G, 2); end这段代码里的G矩阵每迭代一次就做一次Kronecker幂扩张,本质上是在复现Polar编码的蝶形变换。注意info_bits和frozen_set这两个向量不能有交集,它们的并集必须是1:N的全集,否则生成的码字直接错乱。实际5G NR标准里信息位选择用Q函数计算巴氏参数,但这套工程用的是离线算好的frozen_set,存在cellsearch.mat的frozenTable字段里,好处是省去每次仿真都重算极化的时间。
2.2 SC译码的LLR递归更新
译码端实现的是SC(Successive Cancellation)译码器,Polar编码目录下SCDecoder.m的核心是LLR的递归计算。与BCJR这类最大后验算法不同,SC译码按比特序号依次判决,第i个比特的LLR由两部分组成:一是接收符号直接给出的信道LLR,二是前面i-1个已判决比特通过蝶形结构传递过来的冻结约束。关键代码是递推式(用f函数和g函数两个分支):
function LLR_out = sc_node_update(LLR_left, LLR_right, u_left_hat) % f 节点:用于偶数位,对应异或约束 LLR_f = 2 * atanh(tanh(LLR_left/2) .* tanh(LLR_right/2)); % g 节点:用于奇数位,需要用到已判决的左侧比特 LLR_g = LLR_right + (-1)^u_left_hat .* LLR_left; end在实际仿真中,f函数里的tanh计算在长码长下会有数值下溢风险,工程里用了近似公式sign(a)*sign(b)*min(abs(a),abs(b))替换,代价是大约0.1 dB的增益损失,但仿真速度提升一倍以上。建议你在修改这段代码时把近似开关做成一个参数SC_approx_enable,对比两种模式下的BLER差异。
2.3 速率匹配与CRC附加的工程处理
3GPP NR的Polar码不是裸的Arikan码,还要做速率匹配——从母码长度N中按QAM映射顺序打孔或缩短出实际传输的E比特。这套源码里rate_match.m实现的是最简单的BPSK映射下均匀打孔:对编码后的encoded按puncture_pattern向量做索引选取,puncture_pattern在系统参数.m里通过nrPolarRateMatch接口生成(如果MATLAB版本低于R2022a,需要把该函数替换为自实现的比特选择)。
CRC部分要注意PolarEncoder.m的输出encoded长度是N + crc_length还是N取决于调用方式:工程里默认在Runme.m中先调用crc_attach.m对信息比特附加24位CRC(生成多项式0x1864CFB),然后才送进polar_encode。这意味着如果自己写测试脚本复用SCDecoder.m,解码出来的info_bits前K-24个才是真实业务比特,后面24位是CRC校验位——BLER统计时就是靠这24位判断码块是否正确。
3. PSS-SSS联合检测:相关性峰值融合与判决门限
3.1 PSS/SSS序列生成与频域资源映射
PSS用的ZC(Zadoff-Chu)序列根索引在NR里有三个候选值(29、34、37),SSS用的m序列则由PSS索引和小区ID共同决定。工程里发端目录下的pss_generator.m按3GPP TS 38.211第7.4.2节实现,核心参数是频域偏移v = NID2(0到2取值),SSS的加扰序列生成依赖NID1(0到335),所以cellsearch.mat里存的NID1和NID2是同步检测的目标值。
联合检测的输送到收端后,接收机先做频偏估计(用PSS的共轭相关峰),再做时偏估计(用SSS的差分相关峰)。但工程里的联合检测.m没走这条串行老路,而是把PSS和SSS的相关结果在时域上直接相加得到联合判决度量:
joint_metric = abs(xcorr(rx_pss, pss_ref)) .^ 2 + abs(xcorr(rx_sss, sss_ref)) .^ 2; [max_val, max_idx] = max(joint_metric); if max_val > detection_threshold sync_success = 1; else sync_success = 0; enddetection_threshold的取值直接影响PSS-SSS联合检测成功率曲线:设低了虚警多,设高了漏检多。这是个牵一发动全身的参数——联合检测成功率统计的是正确检测到NID1和NID2的仿真帧数占总帧数的比例,而不是相关峰位置是否精确。你会发现工程里默认阈值取0.75(归一化后的相关峰功率),在TU信道低信噪比(SNR < -2 dB)下漏检率上升很快,这时候二段判决法更稳:先用PSS相关峰粗定界,再在粗定时附近搜索SSS相关峰。
3.2 联合检测成功率的统计口径
联合检测成功率这个指标在MATLAB里不是直接用detect = (NID_est == NID_true)判断的,因为PSS检测错了小区组内ID(NID2)但SSS检测正确的情况也存在。工程里把两者拆开:pss_success单独统计NID2的检测正确率,sss_success单独统计NID1的检测正确率,joint_success要求两个都正确才算同步成功。统计代码在Runme.m里循环体的末尾:
% 每次快照(snapshot)统计 if nid2_est == nid2_true && nid1_est == nid1_true joint_detect_cnt = joint_detect_cnt + 1; end pss_success_rate = pss_detect_cnt / total_snapshots; sss_success_rate = sss_detect_cnt / total_snapshots; joint_success_rate = joint_detect_cnt / total_snapshots;如果发现仿真输出的joint_success_rate比pss_success_rate低很多,优先怀疑SSS的加扰序列初始化问题——SSS的生成需要NID2作为输入,如果发端和收端的NID2索引对不上,SSS相关峰直接淹没在噪声里,即使在AWGN信道下也解不出来。
3.3 DMRS辅助的信道估计如何影响后续Polar译码
R_hh_dh_viena_DMRS_frontloaded2LRB.mat里存的是维耶纳(Wiener)滤波器系数,用于DMRS(解调参考信号)信道的插值。这套信道估计用在前导符号(front-loaded DMRS)上,估计出的信道频响H直接影响Polar译码前的LLR计算。联合检测输出的时偏估计值timing_offset_est要反馈给信道估计模块做时域补偿,否则OFDM符号的循环前缀错位会在DFT后引入相位旋转——这个相位误差如果不补偿,SC译码器的LLR置信度会系统性偏差,BLER曲线出现平台。
4. TU信道模型与BER/BLER仿真统计的坑
4.1 TU信道系数文件的使用方式
TU_channel_An11_I.bin和TU_channel_An11_Q.bin是Typical Urban信道模型的实部和虚部时域冲激响应,采样率与仿真带宽有关(工程里是30.72 Msps,对应20 MHz带宽)。读取方式不是MATLAB的load直接导入,而是用fread按float32格式读入,因为.bin是纯二进制文件不带头:
fid = fopen('TU_channel_An11_I.bin', 'rb'); h_real = fread(fid, 'float32'); fclose(fid); fid = fopen('TU_channel_An11_Q.bin', 'rb'); h_imag = fread(fid, 'float32'); fclose(fid); h_tu = complex(h_real, h_imag);.mat文件(R_hh_dh_viena_DMRS_frontloaded2LRB.mat)可以直接load加载,里面包含R_hh(维耶纳滤波的相关矩阵)、dh(时延向量)、viena(维耶纳滤波器系数)三个结构体。注意这个MAT文件里的相关矩阵是基于特定SNR点(通常是10 dB)预计算的,如果仿真时信噪比偏差过大,建议按当前SNR重新生成R_hh,否则信道估计误差在高SNR段会反而成为主导噪声。
4.2 BER与BLER的计算差异
BER和BLER在代码里是两套独立的统计逻辑。BER在调制解调符号级统计,把接收的软比特做符号判决后和发端原始比特对比:
ber = sum(rx_bits ~= tx_bits) / length(tx_bits);BLER统计的粒度是码块(code block),判断依据是CRC校验:
% 译码后做CRC校验 [crc_ok, dec_bits] = crc_check(decoded_info_bits); if ~crc_ok bler_count = bler_count + 1; % 一个码块错误即计数 end bler = bler_count / total_blocks;这里有个容易踩的坑:如果跑的是高吞吐仿真(比如连续发10000个码块),MATLAB脚本在parfor并行循环里统计BLER时会遇到随机数流问题——每个worker的rng默认独立,但如果不显式设置,不同worker产生的信道衰落可能重复。工程的Runme.m里统一在循环开始前调用了rng(seed + snapshot_idx)确保每个快照的随机种子可复现,这个习惯值得留用。
4.3 仿真参数对Polar编解码性能的影响
Polar码的参数(K, N, CRC长度)在不同SNR点的表现差异很大。工程里默认N=512, K=128, CRC=24,在TU信道下BLER=0.01对应的SNR大约比AWGN信道高2-3 dB。调整参数时要注意frozen_set与码长N是绑定的,如果从512改成1024,需要重新生成极化权重表——cellsearch.mat里的frozenTable是按N=512存储的,盲目改N会直接报维度错误。常用的做法是在系统参数.m里增加一个N_target = 1024的配置,用PolarConstruction.m里的calculate_frozen_set(N_target, K)方法临时生成,但生成时间在N=1024时大约需要20秒,高吞吐仿真前先单独跑一次验证生成逻辑。
5. 验证Polar译码器与同步模块联调的三个自查手段
5.1 用极简AWGN链路隔离同步模块的影响
如果发现联合检测成功率曲线异常,先不要急着调Polar译码参数。把Runme.m里的channel_type改成'AWGN',并把TU_channel_An11_I.bin的读取段注释掉,这样链路里没有多径时延,PSS-SSS相关峰应该是干净的冲击响应。此时统计的joint_success_rate如果仍然不到100%,说明问题出在发端PSS序列生成或收端相关检测的索引映射上,跟信道估计和Polar译码无关。我一般会在SNR=0 dB下跑100帧,要求joint_success_rate不低于95%,否则就检查PSS的频域资源映射位置是否与FFT移位有关。
5.2 软比特LLR的可视化检查
Polar译码前把LLR序列画出来(用plot(LLR_in))是排查译码器冻结位集合错误的最快方法:正常情况下,信息位对应的LLR绝对值明显高于冻结位(因为冻结位不携带信息,LLR应接近0)。如果看到所有LLR分布杂乱无章,说明信道估计或解映射环节出了问题——先查R_hh_dh_viena_DMRS_frontloaded2LRB.mat中的维耶纳系数是否与当前SNR匹配,再查LLR计算时除以噪声方差N0是否用了1/SNR_linear而不是1/(2*SNR_linear)(BPSK和QPSK的LLR公式里噪声方差系数不同)。
5.3 BLER曲线的置信区间确认
仿真输出的BLER曲线在低BLER区(低于1e-3)波动剧烈,因为要统计到足够多的错误块才能让曲线平滑。工程里bler_count的统计位宽是uint32,如果跑到total_blocks为百万级,需要把计数器改成uint64防溢出。经验法则是:total_blocks至少是10 / target_BLER,比如要统计BLER=1e-3,至少发10000个码块。最后检查Runme.m里有没有把skip_first_frames参数设为大于0——前几帧因为信道估计器收敛未稳,BER会偏高,通常跳过前10帧再开始统计最稳妥。
本文还有配套的精品资源,点击获取