简介:面向3G/4G/5G广播通信仿真的MATLAB源码包,可帮助通信工程学生、算法工程师及物理层研究者快速搭建广播信道仿真与验证环境。压缩包内共7个文件,全部为.m脚本,整体仅12KB;代码围绕广播信道处理展开,包含广播拓扑生成、统计与动画展示、信息统计等辅助模块,并提供有无信道编码CA/noCA两种实现,便于对比不同前向纠错配置下的传输效果。目前已有204人学习/浏览,适合用作课程设计或毕业设计的基础工具:运行后可观察3G WCDMA、4G OFDM/MIMO、5G NR/NOMA等不同制式下广播信道的处理流程与性能差异,通过修改调制方式、信道参数等变量,还能进一步分析多径衰落与干扰对系统的影响。无论是快速理解广播信道原理,还是为5G及后续通信系统仿真预留接口,这套脚本都能提供实用起点。
1. 3G/4G/5G开发中的广播信道:一份MATLAB仿真资源解决BCH/PBCH链路闭环
“3G/4G/5G开发 matlab”这个关键词对应的活,往往不是跑通一个PSS检测就结束了,而是要把广播信道这块硬骨头啃完。去年我做小区搜索项目,PSS/SSS检测算法调得挺顺,一进到PBCH就直接翻车——MIB的24个比特打包好了、CRC也过了,卷积编码之后速率匹配的输出索引老是对不上,解出来的全是错位比特。后来拿到这份Broadcast.rar,它把3G的BCH、LTE的PBCH、5G NR的SSB/PBCH三套广播信道代码放在同一目录里,用MATLAB从高层比特一路跑到OFDM时域波形,正好补上了整个链路里最难验证的一环。资源不是零散函数,而是带运行脚本、参数配置和中间量检查点的整链路仿真,适合通信物理层算法工程师、无线通信方向研究生,以及做NR小区搜索和MIB解码的人。这篇笔记,我把拆包、复现和踩坑过程完整写出来,里面所有代码块都能直接拿去对照自己的工程。
2. 拆开 Broadcast.rar:三层目录、运行入口与仿真数据流
拿到压缩包第一件事不是解压就跑,而是先看清目录怎么分。广播信道在三个代际里物理层处理路径完全不同,如果所有脚本平铺在一个文件夹里,改参数都会互相踩。
2.1 目录怎么分:3G、4G、5G三套代码用common函数隔离
解压后目录结构是这个资源最值得先看的点:
Broadcast/ ├── 3G_WCDMA/ │ ├── bch_encode.m │ ├── bch_decode.m │ ├── pccpch_modulate.m │ └── run_bch.m ├── 4G_LTE/ │ ├── pbch_tx.m │ ├── pbch_rx.m │ ├── mib_unpack.m │ └── run_pbch.m ├── 5G_NR/ │ ├── ssb_generate.m │ ├── pbch_nr_encode.m │ ├── dmrs_ssb_scramble.m │ └── run_ssb.m ├── common/ │ ├── crc16.m │ ├── conv_code.m │ ├── rate_match.m │ └── ofdm_modulate.m └── README.md目录设计的关键是把跨代际能复用的函数抽到common里。CRC16、卷积码、OFDM调制这三块在3G/4G/5G里都不变,写在公共目录里既能保证版本一致,也避免同一个功能在三个目录里各写一份,后续改逻辑要改三处。而3G_WCDMA、4G_LTE、5G_NR三个目录各自只放本制式特有的代码,比如3G里的P-CCPCH信道化、4G里的PBCH加扰、5G里的SSB栅格映射,互不引用。
三个运行脚本输出的验证目标也做了递进设计。run_bch.m只验证BCH经过编码、信道化后能正确解回系统信息,不涉及OFDM;run_pbch.m跑完整的bit到grid再到时域波形,输出MIB的BER曲线;run_ssb.m则在SSB层面验证多波束和PBCH组合链路。我比较喜欢这个递进:先把最简单的制式跑通,建立对common函数的信心,再往复杂链路上加,遇到定位问题也容易隔离。
2.2 运行入口与全局配置:run_all.m是资源的主控脚本
资源带了一个顶层入口run_all.m,这个脚本做的事其实很克制,就是按顺序调用三个制式各自的运行脚本:
% run_all.m —— 依次执行三套广播信道仿真 config = broadcast_config(); fprintf('== 3G WCDMA BCH ==\n'); run_bch(config.wcdma); fprintf('== 4G LTE PBCH ==\n'); run_pbch(config.lte); fprintf('== 5G NR SSB/PBCH ==\n'); run_ssb(config.nr);这段代码的逻辑很简单,但有两点值得学。一是所有参数都通过broadcast_config()返回的config结构体传入,而不是在每个脚本里定义全局变量,这样换一组测试参数不需要改业务代码;二是三个脚本都不写死路径,靠config里的字段定位数据目录,目录结构调整后只需改配置位置。
config结构体里的参数按制式分开管理,这点在做联合仿真时特别有用。config.wcdma里存的是OVSF码索引、TTI长度、信道化扩频因子;config.lte里是FFT点数、小区ID、天线端口数、PHICH配置;config.nr里则是SSB波束数L_max、子载波间隔、SSB栅格候选频点。每个字段都有默认值,脚本开头会打印当前生效的配置,方便复现时对照。
2.3 数据流设计:中间量都落在工作区,断点调试很方便
这个资源在数据流上的处理方式是它比一般源码包好用的主要原因。每跑完一个阶段,中间量都会以工作区变量方式保留,并带标准检查项:
| 阶段 | 中间量 | 维度 | 检查标准 |
|---|---|---|---|
| BCH编码后 | codedBits | 120×1(LTE) | 与协议期望的尾比特一致 |
| 速率匹配后 | rmBits | 1920×1(LTE) | 4帧分段每段480bit |
| 加扰后 | scBits | 1920×1(LTE) | 与Toolbox输出一致 |
| 调制符号 | qpskSym | 960×1(LTE) | 星座点落在±1/√2 |
| RE索引 | reIdx | 240×1(LTE) | 避开RS的RE位置 |
| OFDM波形 | txWaveform | NFFT×4 | 时域波形峰值连续性正常 |
这些中间量是定位问题的关键帧点。我调试时习惯每步都save一个.mat文件,然后和MATLAB通信工具箱(LTE Toolbox / 5G Toolbox)的输出做逐位比对。比如速率匹配输出rmBits如果和lteRateMatchConvolution的结果对不上,问题肯定出在子块交织或k0计算,而不是后面的加扰和调制。数据流越清晰,定位越准。
3. 复现 LTE PBCH 发射链路:MIB打包、卷积码、速率匹配与RE坐标
LTE PBCH是整个广播信道链路里最适合用来练手的一块,因为它流程完整且每一段都有协议明文定义:24bit MIB加CRC后进卷积编码,速率匹配展成1920bit,分4帧发送,每帧240个QPSK符号映射到子帧0第二个时隙的中心72子载波上。复现时按这个顺序拆开做,每个环节的参数都能独立验证。
3.1 MIB的24比特打包:先把三个协议字段按位序排对
LTE MIB在RRC层的字段是:3bit的dl-Bandwidth、1bit的phich-Duration、2bit的phich-Resource、8bit的systemFrameNumber,外加10bit的spare,总共24bit。这24bit作为BCH传输块,加上16bit CRC后得到40bit,之后才进入卷积编码器。
% mib_pack.m —— 构造24bit MIB(RRC层语义 → 物理层bit) function mibBits = mib_pack(bwIdx, phichDur, phichRes, sfn) % 各字段均转成3/1/2/8位二值行向量 bwBits = de2bi(bwIdx, 3, 'left-msb'); durBits = de2bi(phichDur, 1, 'left-msb'); resBits = de2bi(phichRes, 2, 'left-msb'); sfnBits = de2bi(mod(sfn, 1024), 8, 'left-msb'); mibBits = [bwBits, durBits, resBits, sfnBits, zeros(1,10)]; end这里最容易被坑的是de2bi的比特序参数。de2bi默认输出是LSB在前,也就是right-msb,而3GPP协议里的bit串定义是MSB在前(bit a_0是最高位)。如果漏了'left-msb',整段MIB排序会反转,卷积编码器出来的比特和协议对不上,而且这类错误非常隐蔽——CRC在解调侧依然能过,但解开的MIB字段是乱序拼起来的。
注意systemFrameNumber这里只取8bit,这是因为LTE MIB只携带SFN的高8位,低2位不直接进MIB载荷,而是通过PBCH在40ms周期内4帧的发送分段位置体现。解码端靠盲检SFN mod 4的值来恢复低2位,这个设计在3.2节速率匹配里会看得更清楚。
3.2 卷积编码与速率匹配:40bit进,1920bit出,4帧怎么分段
LTE PBCH用的是约束长度7的1/3码率卷积码,生成多项式[133 171 165]是八进制表示,对应36.212里定义的g0、g1、g2。40bit进去,编码后得到120bit。真正决定PBCH复杂度的在后面:速率匹配要将120bit展成1920bit输出,按40ms周期分4帧发送,每帧取480bit。
% pbch_rate_match.m —— 卷积编码 + 循环缓冲速率匹配 function rmBits = pbch_rate_match(bchBits) % bchBits:40bit(含CRC) coded = conv_code(bchBits, 7, [133 171 165]); % 生成多项式 e = 1920; % PBCH每40ms输出比特数 k0 = 0; % RV0时k0=0 rmBits = zeros(e, 1); for i = 0 : e-1 rmBits(i+1) = coded(mod(k0 + i, length(coded)) + 1); end end代码里的循环模拟的是循环缓冲读取,但实际工程实现不能这么简单。LTE的速率匹配要先把编码输出的三路比特各自做子块交织,交织后的序列再按循环缓冲方式拼接,k0是读取起点。PBCH固定使用RV0,k0=0,所以起点问题被简化了;但如果这段代码被复用去做PDSCH,k0就必须根据冗余版本和软缓冲能力计算,直接线性读取会错得很离谱。
一个值得做的验证实验是打印k0位置后前8个输出索引,和lteRateMatchConvolution的结果做对比。如果一致,说明速率匹配链路已经通了;如果不一致,优先检查子块交列表是不是按36.212表5.1.4-1生成的。速率匹配通过后,1920bit按SFN mod 4=0/1/2/3分成4段,每段480bit,分别放在4个10ms帧里发送,这就是SFN低2位在物理层的体现。
3.3 加扰、QPSK调制与RE映射:240个RE必须落在子帧0的第二个时隙
LTE PBCH的调制方式固定是QPSK,所以1920bit映射为960个QPSK符号,平摊到4帧里每帧240个符号,对应240个RE。这240个RE的坐标约束说得很明确:位于子帧0的slot1(第二个时隙),取该时隙的前4个OFDM符号,频域上限制在中心72个子载波(6个PRB)。
% pbch_map.m —— 把QPSK符号填入子帧0第2时隙的RE function txGrid = pbch_map(qpskSym, pci, nSlot) % 已假设 txGrid 是 12×N 的时隙格栅,这里演示单个RB sub = [0 1 2 3]; % PBCH占第1时隙的4个OFDM符号 reIdx = 6*12 + (0:11); % 72个子载波中间段,6个RB % 所有RS位置用NaN标记,后续填数据时跳过 [I, J] = meshgrid(reIdx + 1, sub + 1); % +1是因为MATLAB下标从1起 ... end这里有几个细节必须处理对。第一,PBCH所在的72个子载波不是从0到71,而是以DC子载波为中心,左右各36个,对应频域上-36到+35的位置,映射到矩阵时要加偏移。第二,每个OFDM符号上还穿插着小区专用参考信号(Cell-specific RS),RS的位置和天线端口数强相关,PBCH的RE分配必须跳过这些RS位置,否则数据会覆盖参考信号,接收端信道估计直接崩。
第三,加扰序列每帧重新初始化,初始值只由小区ID决定,但每10ms帧的加扰输出是重新生成的,4帧之间用的序列相同。这里容易产生一个误会:有人以为是SFN低2位参与加扰初始化,实际参与的是速率匹配分段,加扰序列只在每帧起始处重置,和SFN没有直接映射关系。
| 对比项 | LTE PBCH | NR PBCH |
|---|---|---|
| 所在位置 | 子帧0第2时隙前4符号 | SSB的第1、2、3符号 |
| 频域占用 | 中心72子载波 | 20个PRB(240子载波) |
| 信道编码 | 卷积码 | Polar码 |
| 调制方式 | QPSK | QPSK |
| 编码后速率 | 120bit→1920bit | Polar编码后按SSB时隙分配 |
| SSB索引承载 | 无 | DMRS携带低3位,PBCH载荷携带高3位 |
4. 换到5G NR:SSB栅格与PBCH的Polar码链路
NR里PBCH不再是独立的物理信道,而是被塞进SSB(SS/PBCH Block)里,和PSS、SSS一起在时频栅格里周期性出现。这意味着如果只按LTE那套MIB+子帧的思路去解,第一轮就会卡在SSB索引盲检上。NR的PBCH从编码到加扰,处处都在暗示“这是一个多波束系统”。
4.1 NR的BCH载荷:MIB不单独发,SSB索引藏在DMRS和加扰里
NR的BCH传输块大小A=32bit,这和LTE的40bit(24bit MIB+16bit CRC)完全不同。其中MIB相关字段占一部分,剩下的位置放半帧指示位和SSB索引的高位。L_max=64时,SSB索引总共6bit,低3位不由PBCH载荷承载,而是通过PBCH解调参考信号(DMRS)的序列偏移来传递;高3位则进入BCH载荷。L_max=8或4时,这个位数分配会按比例收缩,盲检时要根据SSB pattern切换假设。
% nr_bch_pack.m —— 组装NR PBCH的32bit BCH输入(结构示意) function bchIn = nr_bch_pack(mibStruct, sfn, ssbIdx, halfFrame) mibBits = pack_mib(mibStruct); % 按38.331字段顺序打包 hfBit = bitand(halfFrame, 1); ssbHi = de2bi(ssbIdx, 6, 'left-msb'); bchIn = [mibBits(:); hfBit; ssbHi(1:3).']; % 高3位进BCH载荷 end这段代码我标了“结构示意”,是因为不同版本的RRC字段定义有小差异,字段间到底是22bit还是23bit也有历史变更。真正写工程时不要自己数位,最稳妥的方式是直接查38.331的MIB定义,把每个字段按结构体组装,然后交给编码函数。这里的关键认知是:NR的BCH载荷里没有“spare占位”这种为了对齐而存在的东西,每个bit都有明确用途。
加扰的部分更要注意。NR PBCH加扰序列的初始值不是单纯的小区ID,而是由小区ID、SSB索引低位、半帧指示组合生成。也就是说,同样的小区ID在不同SSB索引下,加扰序列完全不同。LTE里那种“初始化一次,整帧复用”的思维在这里直接失效。
4.2 SSB栅格与subcarrierOffset:SCS=15kHz和30kHz时的频域对齐
NR的SSB在频域占20个PRB,总共240个子载波。四个OFDM符号里,符号0放PSS,符号1放PBCH,符号2放SSS加PBCH,符号3放PBCH,DMRS在符号1和符号3上按每4个子载波间隔插入。PBCH的解调需要先知道DMRS的位置,而DMRS位置和SSB索引低位相关,所以接收端必须先做一轮DMRS相关,确定索引低位后再开始解PBCH。
| SSB内OFDM符号 | 内容 |
|---|---|
| 符号0 | PSS |
| 符号1 | PBCH + DMRS |
| 符号2 | SSS + PBCH |
| 符号3 | PBCH + DMRS |
频域上另一个坑是subcarrierOffset。MIB里的ssb-SubcarrierOffset字段一共4bit,指示SSB相对公共资源块网格的频域偏移,单位是子载波。这个偏移直接影响后续SIB1的PDCCH/PDSCH解调起始位置。在SCS=15kHz和SCS=30kHz两种配置下,SSB候选频点会落在不同的GSCN栅格上,同样的小区ID可能对应完全不同的子载波偏移值。我处理这个问题时用的方法是在配置里显式传入ssbOffset,不让代码自己去猜,因为猜错的代价是整个SIB1链路瘫痪。
4.3 用nrBCH函数先跑通,再回头对齐手写实现
NR的Polar码和LTE的卷积码完全不是一个量级,手写Polar编码器不是不行,但没必要作为第一版工作。较新版本的MATLAB 5G Toolbox直接提供了nrBCH和nrPBCH函数,我的建议是把它当标准答案用:
% 对照脚本:先用5G Toolbox跑出标准PBCH符号 cfg = struct('NCellID', 128, 'SubcarrierSpacing', 30); bchBits = nrBCH(mibBits, sfn, ssbIdx, Lmax); % 返回BCH编码后的bit pbchSym = nrPBCH(bchBits, ssbIdx, cfg); % 返回PBCH调制符号这段代码的调用形式我特意没写死,因为不同小版本的Toolbox对入参结构要求不同。核心流程是:先用nrBCH完成BCH信道编码,再用nrPBCH完成加扰和QPSK调制,得到标准符号;然后把自己手工链路的输出和它做逐符号对比。误差小于1e-6,说明加扰、调制、比特序全部对齐;对不上时,优先查BCH载荷的字段顺序是否按38.212的bit序列定义排列,这是所有误差里占比最高的来源。
5. 避坑与排查:LTE/NR广播信道仿真五个高频翻车点
广播信道链路长、中间量多,真跑起来之后翻车的点高度集中。这里写五条我实际踩过、也帮别人排查过的经验,每一条都按现象、原因、解决三个层面展开,可以直接对照自己的代码。
5.1 加扰序列和速率匹配分段混为一谈
现象:LTE PBCH解调后误码率一直下不去,CRC偶尔通过但MIB字段解出来是乱序的;换一个SFN初值,结果又变。
原因:把SFN低2位塞进了加扰初始值。LTE PBCH的加扰序列初始值只由小区ID决定,SFN低2位影响的是速率匹配输出在40ms周期里的分段位置,两者不是一回事。
解决:把加扰初始值固定为c_init=N_ID_cell,每帧重置序列但不要改初始值;速率匹配的1920bit按SFN mod 4分成4段,每帧取对应段。验证时打印第一帧的加扰序列前10bit,再和ltePBCH函数内部逻辑对比,立刻能看出是谁错了。
5.2 NR的DMRS偏移假设漏了候选,SSB索引盲检失败
现象:多波束仿真里,SSB索引偶尔解对偶尔解错,L_max=8配置下错误率尤其高;MIB本身能解出,但索引值和真实发射波束对不上。
原因:PBCH DMRS的位置随SSB索引低位变化,盲检时只假设了一种DMRS偏移。L_max=64时有8种假设,L_max=8时也有2种假设,每漏一种就有一定概率把索引解偏。
解决:把盲检写成偏移遍历循环,对每个候选偏移做一次DMRS相关,相关峰值最大的偏移作为SSB索引低位候选,再用CRC校验决策。这个遍历的代价不高,因为DMRS相关只需要在本地频域做乘法累加,不需要完整解调整段PBCH。
5.3 速率匹配k0取错,前480bit从错误起点开始读
现象:LTE PBCH解码时CRC能过,但解出的MIB带宽字段、PHICH字段全是乱的,每当改动天线端口数配置,错误位置跟着变。
原因:速率匹配的循环缓冲读取没有按36.212的子块交织索引表做,而是直接按线性顺序从头读。RV0的k0确实是0,但循环缓冲的w_k序列本身是交织后的结果,不是编码输出的线性拷贝。
解决:严格按协议构建子块交织后的w_k序列,k0=0处开始读480bit,逐帧平移。自查方法是用lteRateMatchConvolution的输出来对比自己的rmBits,只要前8个bit不同,就说明交织表写错了。
5.4 天线端口数假设错误,MIB解出来却解不了PDSCH
现象:LTE链路里PBCH解调正常,MIB也能解出,但后续PDSCH解调一直失败,RB解映射错位严重。
原因:MIB里的天线端口信息不是普通字段,而是通过CRC掩码承载的。解码时如果只按单端口假设去解PBCH,CRC同样可以通过(因为掩码差异只在CRC计算上),但解出的端口数不对,后续所有依赖天线端口数做RS位置推断的环节全错。
解决:对1/2/4端口三种CRC掩码各做一次尝试,CRC校验通过的那个掩码就是天线端口配置。这个循环必须在MIB解调的第一步做,而不是最后一步,否则后面全部白算。
5.5 OFDM调制双重循环拖垮仿真,多波束场景直接跑不完
现象:把NR的64波束配置跑起来后,一个SSB的生成要几十秒,整帧仿真预计几小时起步,压根没法迭代。
原因:RE映射时用两层for循环逐个RE填数据,OFDM调制又单独对每列做ifft,计算量呈二次方增长。
解决:RE映射改成矩阵赋值,一次性把240个符号填进grid对应位置;OFDM调制用矢量化,整块时域波形一次性ifft生成。多波束的循环改成parfor并行,配合资源里common目录的ofdm_modulate函数,实测能提速10到20倍。
6. 进阶用法:把PBCH模块接进小区搜索级联流程,并做多波束回归
6.1 级联顺序:PSS→SSS→PBCH→MIB,时序同步如何对齐
小区搜索不是单独把PBCH拿出来解,而是按PSS检测符号边界、SSS检测小区ID、PBCH解MIB的顺序逐级推进。PBCH的起始位置依赖PSS给出的OFDM符号定时偏差,定时偏差超过CP范围,PBCH解调性能会断崖式下降。我通常的做法是先把资源里的run_ssb跑出一个理想SSB网格,做成参考波形,再往接收端加入-5dB信噪比和时延扩展,用来验证级联链路里时偏估计的容忍度。
6.2 多波束回归:把单波束SSB改成8波束扫描
这个资源的5G_NR目录默认是单波束仿真,但NR实际部署中SSB burst会携带多个波束。我把它改成候选波束循环,用DMRS和PBCH联合相关值做波束选择:
% 多波束SSB解码:遍历候选SSB索引,取最大相关峰值 for idx = 1:Lmax [mibOut, scorr(idx)] = decode_ssb(rxWaveform, candidateGrid(idx)); end [~, bestIdx] = max(scorr);这段代码本身不复杂,真正需要注意的是candidateGrid的构建:每个候选SSB索引都要先用DMRS偏移生成对应的参考序列,再用该序列去解扰PBCH。相关峰值最大的索引大概率就是真实SSB索引,如果CRC再通过,这个判断就基本坐实了。跑完这个扫描后,还可以顺手画一张波束相关值谱,能直观看到多径环境下的波束区分度。
从那以后,我每次跑PBCH链路都会强制做一遍“同一信噪比下Toolbox盲检对比手写盲检”的回归,不为别的,就为了不在误码率曲线上浪费半夜的调试时间。希望帮到你。
本文还有配套的精品资源,点击获取