3、物理层(PHY)关键算法:下行OFDMA与上行SC-FDMA、NPBCH/NPDCCH/NPDSCH信道处理、MIB-NB与SIB1-NB解析
各位同学,咱们今天聊聊NB-IoT物理层最核心的几个算法。说实话,这部分内容在协议栈里属于「硬骨头」,但啃下来之后,你对整个系统的理解会上一个台阶。
我个人习惯把物理层比作「无线通信的骨架」。下行怎么发、上行怎么收、控制信息怎么传、系统消息怎么读——这些搞清楚了,移植和优化才有方向。
3.1 下行OFDMA与上行SC-FDMA:为什么这么设计?
先问大家一个问题:为什么NB-IoT下行用OFDMA,上行却用SC-FDMA?
OFDMA(正交频分多址)说白了就是把带宽切成很多小格子。每个格子叫子载波,NB-IoT一个载波180kHz,正好15个子载波。下行用OFDMA,好处是频谱效率高,抗多径能力强。我在项目中遇到过,有些模组在室内深覆盖场景下,OFDMA的频域调度能明显提升解调成功率。
但上行就不一样了。终端设备功率有限,OFDMA的峰均比(PAPR)太高,对功放要求苛刻。所以3GPP选了SC-FDMA(单载波频分多址)。
核心区别一句话:
- OFDMA:频域生成,时域发送,PAPR高
- SC-FDMA:时域生成,频域映射,PAPR低
嗯,这里要注意:SC-FDMA其实就是在OFDMA前面加了个DFT预编码。我刚开始做移植时,总把这两者的FFT/IFFT顺序搞反,结果解调出来的数据全是乱的。后来我养成了一个习惯——画框图,把DFT和IDFT的位置标清楚。
避坑指南:我曾经在移植上行接收链路时,发现SC-FDMA解调后星座图总是旋转的。查了两天才发现,是DFT长度和子载波映射没对齐。记住:DFT点数等于分配的RB数乘以12,不是固定值。
3.2 NPBCH信道处理:系统信息的「敲门砖」
NPBCH(窄带物理广播信道)承载的是MIB-NB。终端开机第一件事就是找这个信道。你想想看,如果NPBCH解不出来,后面所有流程都走不下去。
NPBCH的处理流程包括:CRC添加、信道编码(咬尾卷积码)、速率匹配、加扰、调制(QPSK)、层映射、预编码、资源映射。我建议重点关注两个地方:
- 加扰序列生成:用物理小区ID(PCID)初始化。移植时记得检查PCID是否从同步信号正确获取。
- 资源映射位置:NPBCH固定占用前4个OFDM符号,频域位置在6个PRB的中间72个子载波。
我记得有一次调试,终端始终读不到MIB。抓log发现NPBCH的CRC校验一直失败。后来定位到是速率匹配的比特选择算法实现有bug——3GPP 36.211里那个循环缓冲区的读取顺序,很容易写错。
3.3 NPDCCH信道处理:下行控制的核心
NPDCCH(窄带物理下行控制信道)负责调度信息。终端要监听这个信道,才知道什么时候收数据、什么时候发数据。
NPDCCH的难点在于搜索空间(Search Space)的设计。NB-IoT定义了两种搜索空间:
| 类型 | 用途 | 聚合等级 | 重复次数 |
|---|---|---|---|
| USS(用户专用搜索空间) | 单播调度 | 1, 2 | RRC配置 |
| CSS(公共搜索空间) | 广播/寻呼 | 2 | 固定 |
我个人习惯在移植时,把搜索空间的盲检逻辑单独封装成一个模块。因为这里涉及DCI格式0/1的区分、RNTI的匹配、以及重复次数的累加。你想想看,如果盲检逻辑写成一团乱麻,后面优化性能时根本无从下手。
警告:NPDCCH的重复次数可以高达2048次。在深度覆盖场景下,终端需要长时间监听同一个候选位置。如果协议栈没有处理好休眠唤醒机制,功耗会爆炸。我曾经见过一个方案,终端在NPDCCH监听期间一直保持全速运行,结果电池撑不过三天。
3.4 NPDSCH信道处理:用户数据的「运输车」
NPDSCH(窄带物理下行共享信道)承载用户数据。它的处理流程和NPBCH类似,但多了HARQ(混合自动重传请求)机制。
NPDSCH的HARQ进程数固定为1(NB-IoT简化设计)。这意味着终端在收到数据后,必须等ACK/NACK反馈才能继续接收。我在优化吞吐量时,发现一个关键点:NPDSCH的调度延迟和重复次数直接相关。重复次数越多,单次传输时间越长,吞吐量越低。
举个例子:
- 重复次数=1时,一个子帧传完,吞吐量约20kbps
- 重复次数=128时,需要128个子帧,吞吐量掉到0.15kbps
所以,优化NPDSCH的关键在于:根据信道质量动态调整重复次数。说白了就是「好信道少重复,差信道多重复」。
3.5 MIB-NB与SIB1-NB解析:系统消息的「说明书」
MIB-NB(主信息块)只有34比特,但信息量巨大。它包含:
- 系统帧号(SFN)的高8位
- 超帧号(Hyper-SFN)的部分比特
- 调度信息(SIB1的时频位置)
- 接入限制信息
我建议在解析MIB-NB时,用位域结构体来定义。这样代码可读性强,也方便调试。
typedef struct {
uint8_t sfn_high_8bits;
uint8_t hyper_sfn_2bits;
uint8_t scheduling_info_sib1;
uint8_t access_barring;
uint8_t reserved;
} mib_nb_t;
SIB1-NB(系统信息块1)就更复杂了。它携带小区选择参数、PLMN列表、TAC等信息。SIB1的调度周期是2560ms,但实际传输可能跨多个子帧。我记得有一次,终端在读取SIB1时总是超时,后来发现是SIB1的重复次数和MIB中指示的不一致——嗯,这里要注意,MIB只给出SIB1的起始位置,重复次数由SIB1-NB本身携带。
个人经验:解析SIB1-NB时,建议先解析出「schedulingInfoList」字段。这个字段告诉终端其他SIB(SIB2~SIB5)的调度信息。如果这个字段解析错了,后面所有系统消息都读不到。
3.6 移植与优化实战要点
最后,我总结几个移植和优化时的关键点:
- FFT/IFFT实现:NB-IoT的FFT点数是128(15kHz子载波间隔)。建议用定点数实现,避免浮点运算带来的功耗开销。
- 信道估计:NPBCH和NPDSCH都用小区专用参考信号(CRS)。插值算法推荐线性插值,复杂度低,性能够用。
- 软合并:HARQ的软合并用Chase合并即可。我在项目中试过增量冗余,收益不大,但代码复杂度翻倍。
- 定时同步:NPSS/NSSS的检测算法,建议用互相关+峰值搜索。我曾经优化过这个模块,把搜索步长从1/4采样点放宽到1/2采样点,性能几乎没下降,但计算量减半。
好了,物理层这部分内容就讲到这里。下一章咱们聊聊MAC层的随机接入和调度流程——那又是另一片天地了。