ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

5G NR PRACH规划:根序列、Ncs与时域配置复核

5G NR PRACH规划:根序列、Ncs与时域配置复核

简介:《5GNR随机接入PRACH的规划》是一份面向5G无线网络规划优化人员、通信工程学习者及网络优化工程师的专题文档,围绕随机接入中的前导规划展开,涵盖竞争与非竞争接入、频域前导生成、时域资源配置、前导格式选择、循环移位与根序列规划等要点,帮助读者把小区覆盖半径、前导数量、逻辑根序列索引间隔等参数串联起来。资源包共1个PDF文件,约532KB,内容集中,便于按主题检索和批注。已有2267人学习下载,在5G NR接入参数规划场景中具有一定参考价值。文档结合现网参数示例说明前导配置索引、零相关区配置、限制集类型等设置,并延伸至高速场景限制集A/B与根序列复用规划,适合从事PRACH参数核算、覆盖评估及高铁场景规划的一线读者查阅。

1. 为什么PRACH规划总在“根序列不够用”上翻车

做5GNR随机接入PRACH的规划,最容易翻车的不是公式抄错,而是根序列数量、Ncs和时域配置三者没有对齐。某地网络把小区半径从1.5km改到3.3km,prachConfigIndex没动,zeroCorrelationZoneConfig还停在6,现场接入成功率掉得莫名其妙。翻协议表才发现,Ncs=32时每个根序列只能产生26个前导,64个前导要连续吃掉3个逻辑根序列,邻区如果还按老间隔分配,根序列就撞了。这份PDF把ZC序列、Cv、根序列索引、时域配置和高速限制集串成一条线,还带了规划工具和协议附表,适合做无线规划、参数核查、高速场景优化的从业者拿着直接复算。

2. 拆开PRACH协议骨架:ZC序列、Cv和64个前导的硬约束

2.1 随机接入的两条路:竞争与非竞争

随机接入过程的目的很明确:一是实现UE和gNB之间的上行同步,二是获取Message3的资源。落到规划上,这两个目的会直接决定前导资源怎么分。基于竞争的随机接入里,多个UE可能生成相同的PRACH Preamble并同时到达gNB,gNB要靠后续冲突解决过程判断谁接入成功;基于非竞争的随机接入里,gNB直接指定PRACH Preamble给UE,常见场景是切换,目的是避开冲突。

这两种类型对PRACH规划的影响不一样。竞争场景下,一个小区要凑齐64个有效前导,根序列、Ncs、Cv都要围着这个目标算;非竞争场景下,gNB指定前导,意味着要给专用前导留出余量,不能把64个前导全部按竞争场景分干净。实际做规划时,我一般先把小区按场景分类:普通覆盖小区、切换密集小区、高速小区。普通小区可以按竞争为主来配,切换密集小区要预留专用前导,高速小区还要把限制集和频偏泄露算进去。

随机接入类型触发场景前导来源规划影响
基于竞争初始接入、重建、上行失步UE从可用前导中随机选每小区要凑够64个前导,根序列数量要算足
基于非竞争切换、部分特殊场景gNB指定前导要预留专用前导,不能把64个全部分给竞争

这张表不是让你背概念,而是提醒一件事:64个前导不是永远都能全部拿来给竞争用。资料里的规划工具会把前导总数、专用前导预留、根序列索引放在一张表里核对,手算容易漏掉预留量。

2.2 ZC序列、根序列u和Cv到底谁决定谁

频域PRACH Preamble的生成公式是:

x_{u,v}(n) = x_u((n + C_v) mod L_RA)

其中x_u(n)是ZC序列,u是根序列,L_RA是ZC序列长度,C_v是循环移位。u的取值在长码下是0~837,短码下是0~137。工程参数里填的是逻辑根序列索引号,不是u本身,逻辑索引和u的对应关系要查表,资料里的协议附表就是干这个用的。L_RA取839或139,对应长码和短码。C_v的取值取决于Ncs和限制集类型,由小区半径、应用场景等因素决定。

关键在C_v的数量。非限制集下C_v = v*Ncs,v从0开始取,能产生的C_v数量是floor((L_RA-1)/Ncs)。每个C_v对应一个前导,所以一个根序列u能产生多少个前导,直接由Ncs决定。如果当前u产生的前导不够64个,逻辑根序列索引号加1,用下一个根序列u'继续产生,直到凑满64个。项目正文里的例子是restrictedSetConfig=非限制集、zeroCorrelationZoneConfig=6、Ncs=32,L_RA=839,每个根序列能出26个前导,64/26向上取整等于3,所以一个小区要连续占用3个逻辑根序列索引。

import math def preamble_count_unrestricted(lra, ncs): """非限制集下,单个根序列能产生的PRACH前导数量。""" if ncs == 0: return 1 # Ncs=0时Cv只有0,一个根序列只出1个前导 return (lra - 1) // ncs for lra, ncs in [(839, 32), (839, 0), (139, 32), (839, 46)]: per_root = preamble_count_unrestricted(lra, ncs) need_roots = math.ceil(64 / per_root) print(f"Lra={lra}, Ncs={ncs}, 每根序列前导数={per_root}, 凑64个需根序列={need_roots}")

这段代码只算非限制集。参数lra是ZC序列长度,长码填839,短码填139;ncs是循环移位步长,不是逻辑索引号。输出里Ncs=32、Lra=839时每根序列26个前导,需要3个根序列;Ncs=0时每根序列只有1个前导,需要64个根序列。短码Lra=139、Ncs=32时每根序列只有4个前导,要16个根序列才够64个,这就是短码场景根序列规划更紧的原因。限制集A/B不能直接套这个函数,Cv的计算规则变了,必须查表或用资料里的高速规划工具。

注意:同一zeroCorrelationZoneConfig索引在不同限制集下Ncs不同。项目正文表里索引6在非限制集是32,在限制集A和B是46,不能只记一个32就到处填。

2.3 用一张表把Lra、Ncs、格式绑在一起

PRACH前导格式分长码和短码。格式0/1沿用LTE的格式0/3,格式2/3、Ax、Bx、Cx是NR新引入的,合计长码4种、短码9种。长码用Lra=839,短码用Lra=139。格式按覆盖场景、最大覆盖半径规划,不是随便挑一个索引。Ncs表有三张,分别对应子载波间隔1.25kHz、5kHz和15*2^u kHz,选错表,Ncs就全错。

参数常见取值作用规划时看什么
L_RA839(长码)、139(短码)ZC序列长度长码广覆盖,短码亚毫秒级、适合密集小区
Ncs0、32、46等循环移位步长由小区半径、多径时延扩展、安全余量反推
zeroCorrelationZoneConfig6等查Ncs表的索引同一索引在不同限制集下Ncs不同
restrictedSetConfig非限制集、限制集A、限制集B是否启用循环移位限制高速场景必须切限制集,350km/h以上看B集
prachRootSequenceIndex逻辑索引号根序列起点不是u本身,要查逻辑索引与u的映射表
prachConfigIndex17等时域PRACH配置决定前导格式、子帧位置、周期

这张表建议贴在规划表旁边。很多人只盯prachRootSequenceIndex,忘了zeroCorrelationZoneConfig和restrictedSetConfig是联动的。项目正文里给的配置组合是prachRootSequenceIndex=I839、restrictedSetConfig=非限制集、zeroCorrelationZoneConfig=6、prachConfigIndex=17,对应Ncs=32、Format=0、子帧4。这是一组能跑通的基线,但换小区半径、换高速场景就要重新算,不能整组照抄。

3. 频域规划落地:Ncs反推半径与根序列数量的手算脚本

3.1 覆盖半径反推Ncs:公式、Tseq和安全余量

小区覆盖半径的公式是:

小区覆盖半径 = (Ncs*Tseq/Lra - 最大多径时延扩展 - 安全余量) * c / 2

Tseq是前导序列持续时间,跟着前导格式走,长码Format0按协议表取800us量级,短码按对应格式查。Lra是序列长度。最大多径时延扩展按场景估,普通城区取几微秒,复杂环境取更大。安全余量是给同步误差、设备时延、多径抖动留的,我一般留3~5us,高速或复杂多径场景会再放大。c是光速。

C = 3e8 # 光速,m/s def radius_km(ncs, tseq_us, lra, delay_us, margin_us): """按Ncs反算小区覆盖半径,单位km。""" guard_us = ncs * tseq_us / lra - delay_us - margin_us if guard_us <= 0: return 0.0 # 保护间隔被吃光,配置不可用 return guard_us * 1e-6 * C / 2 / 1000 # Format0长码示例:Ncs=32,Tseq=800us,Lra=839,多径5us,安全余量5us print(radius_km(32, 800, 839, 5, 5))

这段代码算出来约3.08km。参数ncs=32,tseq_us=800,lra=839,delay_us=5,margin_us=5。项目正文的例子是小区半径3.3km、Format=0、Ncs=32、Lra=839,量级对得上,差异来自多径和安全余量的取值。实际规划时不要拿一个固定余量套所有站型,密集城区多径重,余量留小会把半径算大,现场就会出现循环移位窗重叠。把公式反过来用:已知小区半径,先减多径和安全余量,再反推Ncs,最后去Ncs表里找不小于计算值的索引。

提示:Tseq不要手填一个固定值。长码和短码的Tseq不同,同一格式族里不同格式也不同,资料里的规划工具会按格式自动带出Tseq,手算时至少回到协议表核对一遍。

3.2 根序列够不够64个:手算与脚本复算

一个小区需要产生64个有效的PRACH Preamble。首次用根序列索引号,按所有有效的循环偏移Ncs得到Cv,如果本次根序列不能产生64个前导,就用逻辑根序列索引号的下一个继续产生,直到凑满64个。Cv的计算过程决定了当前小区产生64个前导需要多少个根序列,也决定了根序列索引号怎么规划。

import math def roots_needed(lra, ncs, target=64): """返回(每小区所需根序列数,单根序列前导数)。仅非限制集。""" per_root = 1 if ncs == 0 else (lra - 1) // ncs return math.ceil(target / per_root), per_root for ncs in [0, 32, 46]: need, per_root = roots_needed(839, ncs) print(f"Ncs={ncs}: 单根序列{per_root}个前导,每小区需{need}个根序列")

输出很直观:Ncs=0时单根序列1个前导,每小区要64个根序列,所以规划时不同小区的逻辑根序列索引号间隔至少64个;Ncs=32时单根序列26个前导,每小区3个根序列;Ncs=46时单根序列18个前导,每小区4个根序列。这里的“所需根序列数”是向上取整,实际分配还要加隔离带。比如Ncs=32、每小区3个根序列,相邻小区起始索引至少隔3个,留1个隔离就隔4个。项目正文里的例子是3.3km、Format=0、Ncs=32、Lra=839,每逻辑根序列26个前导,64/26=2.46,向上取整3,和脚本一致。

短码场景要单独算。Lra=139、Ncs=32时单根序列只有4个前导,每小区要16个根序列,逻辑索引消耗很快。短码小区密集时,根序列规划表要留出更多余量,不能按长码的3个根序列习惯去分。

3.3 根序列索引分配:连续索引还是留隔离带

根序列索引分配有两种常见做法:连续分配和留隔离带。连续分配是把一个小区需要的根序列索引连续占满,比如小区A占0~2,小区B从3开始占3~5。留隔离带是在小区之间空1~2个索引,比如A占0~2,B从4开始占4~6。隔离带的作用是降低相邻小区根序列重叠的概率,也给后续扩容留余地。高速场景下,限制集B的Cv不是连续等间隔,某些索引不能用,隔离带要按工具算出来的可用索引组来留。

示例小区起始逻辑索引占用索引数隔离索引数备注
小区A031Ncs=32,每小区3个根序列
小区B431与A隔1个索引
小区C831与B隔1个索引
高速小区D42按限制集B工具结果按组留不能套非限制集连续分配
def allocate_roots(start, roots_per_cell, cell_count, gap=1, max_index=837): """生成非限制集下的根序列分配示例,gap是小区间隔离索引数。""" plan = [] idx = start for cell in range(1, cell_count + 1): if idx + roots_per_cell - 1 > max_index: raise ValueError("逻辑根序列索引越界,需要回绕并避开已用索引") plan.append((f"cell_{cell}", idx, idx + roots_per_cell - 1)) idx += roots_per_cell + gap return plan for row in allocate_roots(0, 3, 5, gap=1): print(row)

这段代码生成5个示例小区的分配,每小区3个根序列,小区间留1个隔离。参数start是起始逻辑索引,roots_per_cell是每小区所需根序列数,cell_count是小区数,gap是隔离数,max_index是逻辑索引上限。实际工程里还要避开已经分配给邻区的索引,资料里的规划工具可以直接导入邻区表做冲突检查。高速场景不要用这个函数,限制集B的可用索引是成组的,项目正文里高铁小区1~2km、Ncs=26的例子,用简易法以19个连续索引为一组,可用根序列索引共15个;用最大法可用19个,具体索引是42、55、71、86、102、116、132、147、166、183、200、214、227、240、254、268、283、297、311。这个结果必须按限制集B的Cv演算来,不能拿非限制集的连续分配去套。

4. 时域规划落地:prachConfigIndex=17的子帧映射与格式选型

4.1 prachConfigIndex=17为什么落在子帧4

PRACH Preamble只能在高层参数prach-ConfigurationIndex给出的时域上传输,具体取决于FR1还是FR2。项目正文给的配置是prachConfigIndex=17,查表可知Preamble format=0,允许UE在任何系统帧的子帧4上发送PRACH。这里的“任何系统帧”不是每个子帧都能发,而是每10ms周期里落在子帧4这一个机会上。时域密度直接决定PRACH容量,子帧4被PRACH占用后,这个子帧上的业务信道调度要避开。

prach_config: prach_ConfigurationIndex: 17 # 高层参数,查表得到格式和子帧 preamble_format: 0 # 由索引17反查,不是单独随意配 subframe: 4 # 索引17对应的时域位置 period_ms: 10 # 任何系统帧的子帧4,周期10ms restricted_set_config: unrestricted zero_correlation_zone_config: 6 prach_root_sequence_index: 0

这段配置片段里,prach_ConfigurationIndex是入口参数,preamble_format、subframe、period_ms都是查表结果,不是独立配置项。现场最容易犯的错是只抄prachConfigIndex=17,却没确认当前频段和FR类型对应的表是不是同一张。FR1和FR2的PRACH配置表不同,同一个索引在不同表里可能对应不同格式或不同子帧。配置核查时,先把索引号、格式、子帧、周期四个字段一起对,再去看根序列和Ncs。

时域密度还要和容量匹配。子帧4每10ms一次,适合普通覆盖小区;密集城区或高话务小区如果PRACH容量不够,要换更密的配置索引,但换了索引可能连格式一起变,Ncs和根序列规划要跟着重算。资料里的协议附表把索引、格式、子帧号放在同一张表里,规划时直接查表比背索引可靠。

4.2 长码与短码格式的选型边界

长码4种、短码9种,选型边界主要看覆盖半径、多径环境、频段和时延扩展。长码Format0/1/2/3适合广覆盖,Format0是项目正文里的基线,Format1/2/3随着序列时长变化,覆盖能力和资源占用不同。短码Ax/Bx/Cx是NR新引入的,适合FR2、密集小区、亚毫秒级时延场景。格式选错,后面Ncs和根序列算得再细也白搭。

格式族L_RA覆盖特点典型场景规划注意
长码Format0/1/2/3839覆盖半径大,时域占用相对长FR1广覆盖、普通宏站先按半径选格式,再反推Ncs
短码Ax/Bx/Cx139亚毫秒级,适合密集部署FR2、室内、密集城区根序列消耗快,要算足64个前导
限制集A按格式高速但非超高速中高速移动场景Cv计算查限制集A表
限制集B按格式超高速,350km/h以上高铁等高速场景用高速规划工具演算Cv,不能手套

选型步骤我一般按这个顺序走:先确认FR1还是FR2,再确认小区半径和多径环境,然后查协议表选格式,接着用覆盖半径公式反推Ncs,最后算根序列数量。这个顺序不能反。先算根序列再选格式,很容易出现格式覆盖不够、Ncs被迫改大、根序列数量又不够的连环翻车。项目正文说前导格式按照覆盖场景、最大覆盖半径规划,这句话是规划顺序的总结,不是一句空话。

4.3 用表格核对FR1/FR2的时域配置

FR1和FR2的PRACH时域配置差异大,核查时用一张表把频段、子载波间隔、Ncs表、典型格式、Lra绑在一起,能少踩很多坑。Ncs表有三张,分别对应子载波间隔1.25kHz、5kHz和152^u kHz。152^u kHz里的u是子载波间隔索引,u=0对应15kHz,u=1对应30kHz,u=2对应60kHz,u=3对应120kHz。选Ncs时先确认当前PRACH子载波间隔落在哪张表,再去查zeroCorrelationZoneConfig。

频段类型典型PRACH子载波间隔Ncs表典型格式L_RA时域核查点
FR1长码1.25kHz、5kHz1.25kHz表、5kHz表Format0/1/2/3839prachConfigIndex查出的子帧和周期
FR1短码15kHz、30kHz15*2^u kHz表A/B/C系列139时域密度是否够,根序列是否够64个
FR2短码60kHz、120kHz15*2^u kHz表A/B/C系列139子载波间隔与格式匹配,避免跨表抄索引

核查时按四步走:确认频段和PRACH子载波间隔;确认prachConfigIndex查出的格式和子帧;确认zeroCorrelationZoneConfig落在正确的Ncs表;确认restrictedSetConfig和场景匹配。四步里任何一步跨表,后面的根序列计算都会偏。项目正文里的prachConfigIndex=17、Format=0、子帧4是一组FR1长码基线,换到FR2不能直接沿用。

5. 避坑与复核:PRACH规划落地前必须走的五条检查

5.1 现象:根序列数量算出来够,现场却接不进

现象是规划表上每小区3个根序列、64个前导算得满满当当,现场接入成功率却上不去,随机接入失败计数偏高。原因通常不是数量不够,而是逻辑根序列索引和u的映射查错,或者restrictedSetConfig填了非限制集但实际场景需要限制集,导致Cv可用数量比预期少。还有一种情况是Ncs表选错,1.25kHz的表套到了5kHz上,算出来的每根序列前导数偏大,实际可用前导不够。

解决时先别急着加根序列,回到三件事:确认prachRootSequenceIndex是逻辑索引还是u,查资料里的映射表;确认restrictedSetConfig和场景一致;确认zeroCorrelationZoneConfig查的是当前子载波间隔对应的Ncs表。三件事对完,再用规划工具复算Cv数量。排查时看随机接入前导检测的虚警和漏检计数,如果虚警高,优先怀疑根序列复用太近;如果漏检高,优先怀疑Ncs和保护间隔。

5.2 现象:小区半径改大后Ncs没跟着改

现象是小区半径从1.5km扩到3.3km,工程参数只改了半径字段,zeroCorrelationZoneConfig还停在原来的索引,Ncs没变。现场表现为远点UE接入困难,近点正常。原因是Ncs决定循环移位窗宽度,半径变大后最大多径时延扩展和安全余量吃掉更多保护间隔,原Ncs对应的窗不够用,循环移位之间发生重叠。

解决时按覆盖半径公式重算:先用新半径加多径和安全余量反推需要的保护间隔,再反推Ncs,最后去Ncs表里选不小于计算值的索引。项目正文的例子是3.3km、Format=0、Ncs=32、Lra=839,这个组合是基线,不是所有3.3km小区都必须用32,多径重的场景安全余量要放大,Ncs可能要大一级。改完Ncs后根序列数量会变,每小区所需根序列数和邻区间隔要一起重算,不能只改一个参数就交表。

5.3 现象:高速场景仍按非限制集配

现象是高速小区、高铁沿线小区还在用restrictedSetConfig=非限制集,规划表上根序列数量够,现场高速移动时接入成功率波动大。原因是高速频偏会让序列相关峰的能量泄露到其它循环移位窗内,泄露的循环移位不能用于产生前导,非限制集下这些窗没有被限制使用,接收性能会下降。R15标准在限制集A的基础上引入限制集B,用于超高速场景,350km/h以上要按B集演算。

解决时先把restrictedSetConfig切到限制集A或B,再按对应Cv规则重新算可用前导。限制集B的Cv不是等间隔,项目正文里高铁小区1~2km、Ncs=26的例子,用简易法以19个连续索引为一组,可规划根序列索引共15个;用最大法可规划19个,具体索引是42、55、71、86、102、116、132、147、166、183、200、214、227、240、254、268、283、297、311。这个结果要用资料里的高速规划工具复算,手算容易漏掉不可用循环移位。

5.4 现象:prachConfigIndex抄了邻区但格式不匹配

现象是新建小区直接抄邻区的prachConfigIndex,参数表上索引一样,现场却出现PRACH时域位置和邻区冲突,或者格式和覆盖需求不匹配。原因是prachConfigIndex不是孤立参数,它绑定了Preamble format、子帧号、周期,FR1和FR2的表还不同。邻区用Format0覆盖广,新建小区是密集城区短码站型,抄同一个索引就会把长码配置带到短码场景。

解决时按当前频段和FR类型查表,确认索引对应的格式、子帧、周期,再和小区覆盖需求对一遍。项目正文的prachConfigIndex=17对应Format0、子帧4、任何系统帧发送,这是FR1长码基线。换到FR2或短码场景,索引要重新选。核查时把prachConfigIndex、preamble format、subframe、period四个字段一起打印出来,任何一项和规划意图不符就退回重选。

5.5 现象:根序列复用距离太近导致虚警

现象是相邻小区根序列索引间隔太小,规划表上每个小区的前导数量都够,现场却出现前导虚警,gNB把邻区前导误判成本区前导。原因是根序列复用距离小于每小区所需根序列数,或者隔离带留得不够。Ncs=32时每小区要3个根序列,如果两个小区起始索引只隔1个,实际占用的索引会重叠。高速场景限制集B的可用索引是成组的,复用距离更不能按非限制集的连续索引去估。

解决时按每小区所需根序列数加隔离带来分配,普通场景至少留1个索引隔离,高速场景按工具算出的可用索引组来留。项目正文里Ncs=0时每小区要64个根序列,不同小区逻辑根序列索引间隔至少64个;Ncs≠0时按floor((Lra-1)/Ncs)算每根序列前导数,再向上取整得到每小区所需根序列数。分配完用规划工具做一次邻区冲突检查,重点看同频邻区、高速沿线小区、切换关系密集的小区。

6. 把规划表变成可复核脚本:三个断言和一条血泪习惯

PRACH规划表交出去之前,我习惯用三个断言做最后一道复核:每根序列前导数够不够、每小区根序列数够不够64个前导、邻区间隔有没有小于每小区所需根序列数。这三个数任何一个不满足,后面参数配得再漂亮都是纸面功夫。

import math def check_prach_plan(lra, ncs, roots_per_cell, index_gap, target=64): """交表前复核:前导数、根序列数、邻区间隔。仅非限制集。""" per_root = 1 if ncs == 0 else (lra - 1) // ncs need_roots = math.ceil(target / per_root) assert roots_per_cell >= need_roots, f"根序列不足: {roots_per_cell} < {need_roots}" assert index_gap >= roots_per_cell, f"邻区间隔太小: {index_gap} < {roots_per_cell}" return per_root, need_roots # 示例:Lra=839,Ncs=32,每小区3个根序列,邻区间隔3 print(check_prach_plan(839, 32, 3, 3))

参数lra和ncs决定单根序列前导数,roots_per_cell是规划表里每小区实际占用的根序列数,index_gap是相邻小区起始索引间隔。断言通过只代表非限制集下数量逻辑成立,限制集A/B还要把Cv可用性加进去,高速场景直接用资料里的规划工具跑一遍。从那以后我每次出PRACH规划表,都先把Ncs、每根序列前导数、所需根序列数、邻区间隔这四个数跑一遍断言,再拿协议附表核对prachConfigIndex和子帧,最后才交给前台。希望帮到你。

本文还有配套的精品资源,点击获取

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进