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)、层映射、预编码、资源映射。我建议重点关注两个地方:

  1. 加扰序列生成:用物理小区ID(PCID)初始化。移植时记得检查PCID是否从同步信号正确获取。
  2. 资源映射位置: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 移植与优化实战要点

最后,我总结几个移植和优化时的关键点:

  1. FFT/IFFT实现:NB-IoT的FFT点数是128(15kHz子载波间隔)。建议用定点数实现,避免浮点运算带来的功耗开销。
  2. 信道估计:NPBCH和NPDSCH都用小区专用参考信号(CRS)。插值算法推荐线性插值,复杂度低,性能够用。
  3. 软合并:HARQ的软合并用Chase合并即可。我在项目中试过增量冗余,收益不大,但代码复杂度翻倍。
  4. 定时同步:NPSS/NSSS的检测算法,建议用互相关+峰值搜索。我曾经优化过这个模块,把搜索步长从1/4采样点放宽到1/2采样点,性能几乎没下降,但计算量减半。

好了,物理层这部分内容就讲到这里。下一章咱们聊聊MAC层的随机接入和调度流程——那又是另一片天地了。