4. 同步信号与PBCH块(SSB):SSB时频结构、PSS/SSS序列生成与检测、PBCH解码与MIB解析
各位同学,欢迎来到第四章。这一章我们要啃的,是5G NR里最基础也最关键的一块——SSB。
说白了,SSB就是终端(UE)开机后,在茫茫频段里找到基站的“第一盏灯”。没有它,后面的一切都无从谈起。我个人习惯把SSB比作“灯塔”,它不光告诉UE“我在这儿”,还告诉UE“我是谁”、“怎么跟我说话”。
好,我们直接进入正题。
4.1 SSB的时频结构
SSB全称是Synchronization Signal / PBCH block。它由三部分组成:
- PSS(主同步信号)
- SSS(辅同步信号)
- PBCH(物理广播信道)
这三兄弟挤在同一个“资源块”里,时域上占4个OFDM符号,频域上占240个子载波(也就是20个RB)。
嗯,这里要注意:PSS和SSS只占127个子载波,但PBCH会占满整个240个子载波。为什么留了空隙?这是为了降低干扰,也是协议设计上的一个巧妙之处。
核心记忆点:
- 时域:4个OFDM符号(符号0~3)
- 频域:240个子载波(20个RB)
- PSS在符号0,SSS在符号2,PBCH在符号1、3以及符号2的两侧
下面这张图,是我自己画的一个SSB时频结构示意,你看一眼就明白了。
你看,PSS和SSS在频域上只占中间127个子载波,两侧留了空。PBCH则把整个240个子载波都占满了。这个结构在协议38.211里定义得清清楚楚,但实际实现时,你还要考虑SSB的频域位置——它不一定在载波正中间。
避坑指南:我曾经在项目里犯过一个低级错误——默认SSB总是在载波中心。结果换了一个频段后,UE死活搜不到信号。后来查协议才发现,不同频段的SSB位置计算方式不一样。记住:SSB的频域位置由GSCN(Global Synchronization Channel Number)决定,不是想当然的。
4.2 PSS序列生成与检测
PSS是UE开机后第一个要检测的信号。它用m序列生成,长度127,对应3个不同的根索引(25、29、34)。
为什么是3个?因为这3个值对应了3个小区ID组内编号(0、1、2)。UE检测到PSS后,就能知道N_ID_2。
生成公式很简单,协议38.211第7.4.2.2节有写:
// PSS序列生成(C语言风格伪代码)
int d_pss[127];
int u[3] = {25, 29, 34}; // 根索引
for (int n = 0; n < 127; n++) {
int m = (n + 43 * u[idx]) % 127;
d_pss[n] = 1 - 2 * x(m);
}
// x序列生成
int x[127];
for (int i = 0; i < 7; i++) x[i] = (i == 0) ? 0 : 1; // 初始值
for (int i = 7; i < 127; i++) {
x[i] = (x[i-3] + x[i-7]) % 2;
}
检测时,UE会在时域上做滑动相关。说白了,就是把接收到的信号和本地生成的3个PSS序列做互相关,找到峰值最大的那个。
我个人习惯用分段相关的方法来降低计算量。因为127点全相关在低信噪比下效果不错,但计算量太大。分段相关可以把复杂度降一个数量级。
注意:PSS检测的难点不在算法本身,而在频偏。UE开机时晶振误差可能达到几十ppm,导致接收信号有较大的频率偏移。我建议你在做PSS检测前,先做一个粗频偏估计,否则相关峰值会被展平。
4.3 SSS序列生成与检测
SSS也是m序列,但它是两个m序列的加扰组合。长度同样是127,但携带的信息更多——它告诉UE小区ID组号N_ID_1(0~335)。
加上PSS检测到的N_ID_2,UE就能算出完整的小区ID:
N_ID_cell = 3 * N_ID_1 + N_ID_2
SSS的生成比PSS复杂一些,因为它涉及两个m序列的循环移位:
// SSS序列生成(简化版)
int d_sss[127];
int m0, m1;
// 根据N_ID_1计算m0和m1
m0 = 15 * (N_ID_1 / 112) + 5 * N_ID_2;
m1 = N_ID_1 % 112;
// 两个m序列
int s0[127], s1[127];
// ... 生成s0和s1(循环移位后的m序列)
// 加扰
for (int n = 0; n < 127; n++) {
d_sss[n] = (1 - 2*s0[n]) * (1 - 2*s1[n]);
}
检测SSS时,UE已经知道了PSS的位置,所以可以利用信道估计来提升检测性能。我一般会先做LS信道估计,然后做差分检测,这样对频偏不敏感。
经验之谈:在实际项目中,SSS检测的误检率往往比PSS高。为什么?因为SSS序列之间的互相关性不如PSS那么理想。我曾经在一个多径严重的场景下,SSS误检率飙到了5%。后来加了时域平滑滤波,才压到0.1%以下。
4.4 PBCH解码与MIB解析
PBCH承载的是MIB(Master Information Block)。MIB虽然只有几十个比特,但包含了UE接入网络的关键信息:
- 系统帧号(SFN)的高6位
- 子载波间隔(common SCS)
- SSB位置(inOneGroup / inOneCell)
- PDCCH配置(CORESET0的频域位置)
- DMRS位置(用于PBCH解调的参考信号)
PBCH的编码过程比较复杂:
- MIB数据(24比特)加上8比特CRC
- 经过极化码编码(速率匹配后432比特)
- 加扰(用小区ID和SFN)
- QPSK调制
- 映射到SSB的PBCH资源上
解码就是逆过程。但这里有个坑——PBCH的加扰序列每80ms变化一次(对应4个SSB突发集)。UE不知道当前是哪个80ms周期内的第几个SSB,所以需要盲检测。
// PBCH解码流程(伪代码)
// 1. 提取PBCH RE
// 2. 信道估计与均衡
// 3. QPSK解调
// 4. 解扰(尝试4种加扰序列)
// 5. 极化码译码(SCL算法)
// 6. CRC校验
// 7. 如果CRC通过,解析MIB
for (int i = 0; i < 4; i++) {
deScramble(pbch_soft, i); // 尝试第i种加扰
polar_decode(pbch_soft, mib_bits);
if (crc_check(mib_bits) == 0) {
// 解码成功
parse_mib(mib_bits);
break;
}
}
避坑指南:极化码译码时,SCL算法的列表大小L直接影响性能和复杂度。L=8时性能已经不错,但L=32才能达到接近理论极限的性能。我建议你在产品中做成可配置的——初始搜索用L=8,稳定后切到L=32。
4.5 完整的SSB检测流程
把上面这些串起来,就是UE开机后的SSB检测流程:
- 时域同步:用PSS做粗同步,找到符号边界
- 频偏估计:利用PSS的重复特性估计频偏
- PSS检测:确定N_ID_2和精确的时域位置
- SSS检测:确定N_ID_1,得到完整小区ID
- PBCH解码:提取MIB信息
- 验证:用DMRS做信道估计,确认PBCH解码正确
整个过程必须在5ms内完成(一个SSB突发集的周期)。嗯,时间很紧,所以算法要高效,实现要优化。
总结一下:
- SSB是UE接入网络的“第一道门”
- PSS提供粗同步和N_ID_2
- SSS提供N_ID_1,组合得到小区ID
- PBCH承载MIB,告诉UE怎么接入
- 整个流程对时延和鲁棒性要求极高
好了,这一章的内容就到这里。SSB这块东西多、细节杂,但只要你把时频结构、序列生成、检测流程这三条线理清楚,后面做实现就不会迷路。
记住我的一句话:SSB检测做不好,UE永远连不上网。所以,多花点时间在这章上,值。
公众号:蓝海资料掘金营,微信deep3321