终端通过PUSCH(物理上行共享信道)向基站发送数据时,信号在终端经过CRC、LDPC、信道编码、速率匹配、加扰、调制、映射等流程后,信号经过复杂的无线信道,基站侧接收到PUSCH数据后进行信道估计及相应的逆操作,还原出终端发射的原始数据。本系列基于Python实现PUSCH简化的信道处理过程,主要是为了学习信道估计过程中的算法知识。
DMRS(解调参考信号)是一个发送端(终端)和接收端(基站)都已知的信号。终端在发送PUSCH数据的同时,会在特定的时频资源上插入DMRS信号。基站收到信号后,通过对比接收到的DMRS和它已知的原始DMRS,就能精确地估算出当前信道的状态。有了这个信道状态,基站就可以对PUSCH上的数据进行反向补偿,即相干解调,从而准确恢复出终端发送的信息。
关于PUSCH DMRS的相关定义详见协议38.211 6.4.1.1相关章节。
如果PUSCH transform precoding disable,DMRS序列公式为:
这里是pseudo-random(伪随机)序列(即一个长度为的Gold序列),在38.211 5.2.1中定义。
[5GNR] 什么是Gold序列
Gold序列是由两个m序列组成,长度为31,协议中关于Gold sequence的定义:
Generic pseudo-random sequences are defined by a length-31 Gold sequence. The output sequence of length , where , is defined by
where
初始化公式如下:
DMRS序列生成相关公式之间的关系

SCS = 30e3# 子载波间隔 (Hz)NUMEROLOGY = 1N_FFT = 2048# FFT点数CP_LENGTH = 144# 循环前缀长度N_SYMBOLS_PER_SLOT = 14# 每个时隙OFDM符号数N_SUBCARRIERS = 100*12# 子载波总数 (100 RB)MODULATION = '16QAM'N_PUSCH_SYMBOL = 14# PUSCH调度symbol数该DMRS配置类包含DMRS所有相关参数和处理函数
classDMRSConfig:"""DMRS配置类, 封装所有DMRS相关的参数和操作"""def__init__(self, dmrs_type, additionalDmrs, n_id, n_scid, n_s, nc, dmrs_type_a_position, ld):# DMRS参数 self.dmrs_type = dmrs_type self.additionalDmrs = additionalDmrs self.n_id = n_id self.n_scid = n_scid self.n_s = n_s self.nc = nc self.dmrs_type_a_position = dmrs_type_a_position # 'pos2' or 'pos3' self.ld = ld # PUSCH调度长度 (0~14个符号) self._validate_parameters() self.dmrsPositions = self._get_dmrs_positions()def_validate_parameters(self):if self.ld < 0or self.ld > 14:raise ValueError("ld must be between 0 and 14")if self.additionalDmrs > 0and self.ld < 4:raise ValueError("Additional DMRS not supported when ld < 4")if self.dmrs_type_a_position notin ['pos2', 'pos3']:raise ValueError("dmrs_type_a_position must be 'pos2' or 'pos3'")if self.additionalDmrs < 0or self.additionalDmrs > 3:raise ValueError("additionalDmrs must be between 0 and 3")def_get_dmrs_positions(self):""" 根据Table 6.4.1.1.3-3确定DMRS符号位置 仅考虑mapping type A && single-symbol DMRS的情况 """ l0 = 2if self.dmrs_type_a_position == 'pos2'else3 dmrsPositions=[#dmrs-AdditionalPosition# 0 1 2 3 [[l0, 0, 0 ,0], [l0, 0, 0 ,0], [l0, 0, 0 ,0], [l0, 0, 0 ,0]], #ld = 4 [[l0, 0, 0 ,0], [l0, 0, 0 ,0], [l0, 0, 0 ,0], [l0, 0, 0 ,0]], #ld = 5 [[l0, 0, 0 ,0], [l0, 0, 0 ,0], [l0, 0, 0 ,0], [l0, 0, 0 ,0]], #ld = 6 [[l0, 0, 0 ,0], [l0, 0, 0 ,0], [l0, 0, 0 ,0], [l0, 0, 0 ,0]], #ld = 7 [[l0, 0, 0 ,0], [l0, 7, 0 ,0], [l0, 7, 0 ,0], [l0, 7, 0 ,0]], #ld = 8 [[l0, 0, 0 ,0], [l0, 7, 0 ,0], [l0, 7, 0 ,0], [l0, 7, 0 ,0]], #ld = 9 [[l0, 0, 0 ,0], [l0, 9, 0 ,0], [l0, 6, 9 ,0], [l0, 6, 9 ,0]], #ld = 10 [[l0, 0, 0 ,0], [l0, 9, 0 ,0], [l0, 6, 9 ,0], [l0, 6, 9 ,0]], #ld = 11 [[l0, 0, 0 ,0], [l0, 9, 0 ,0], [l0, 6, 9 ,0], [l0, 5, 8 ,11]], #ld = 12 [[l0, 0, 0 ,0], [l0, 11, 0 ,0], [l0, 7, 11 ,0], [l0, 5, 8 ,11]], #ld = 13 [[l0, 0, 0 ,0], [l0, 11, 0 ,0], [l0, 7, 11 ,0], [l0, 5, 8 ,11]] #ld = 14 ] tmp = dmrsPositions[self.ld - 4][self.additionalDmrs]return [num for num in tmp if num > 0]在DMRS配置类中定义Gold序列生成函数
defgenerate_gold_sequence(self, c_init, length):"""生成Gold序列 (38.211 5.2.1)""" x1 = np.zeros(31, dtype=int) x1[0] = 1 x2 = np.zeros(31, dtype=int)for i in range(31): x2[i] = (c_init >> i) & 1 c = np.zeros(length + self.nc, dtype=int)for n in range(length + self.nc): c[n] = (x1[n % 31] + x2[n % 31]) % 2# 更新x1 x1_shift = (x1[3] + x1[0]) % 2 x1 = np.roll(x1, -1) x1[-1] = x1_shift# 更新x2 x2_shift = (x2[3] + x2[2] + x2[1] + x2[0]) % 2 x2 = np.roll(x2, -1) x2[-1] = x2_shiftreturn c[self.nc:self.nc+length]在DMRS配置类中定义DMRS序列生成函数,主要调用gold序列生成和调制。在5G中参考信号都是使用QPSK调制方式,协议规定每两个bit映射成一个复数调制符号,
该生成函数按DMRS symbol被调用,每次生成一个symbol的DMRS复数信号
defgenerate_dmrs_sequence(self, length, l):"""生成DMRS序列 (38.211 6.4.1.1.1)""" c_init = (2**17 * (14 * self.n_s + l + 1) * (2 * self.n_id + 1) + 2 * self.n_id + self.n_scid) % (2**31) gold_seq = self.generate_gold_sequence(c_init, 2 * length) r = np.zeros(length, dtype=complex)for n in range(length): r[n] = (1 - 2 * gold_seq[2*n]) / np.sqrt(2) + 1j * (1 - 2 * gold_seq[2*n + 1]) / np.sqrt(2)return r该函数按DMRS symbol调用generate_dmrs_sequence() 生成该符号上的DMRS序列
defmain():# 创建DMRS配置 dmrs_config = DMRSConfig( dmrs_type=1, additional_dmrs=2, n_id=1000, n_scid=0, n_s=0, nc=1600, dmrs_type_a_position='pos2', ld=N_PUSCH_SYMBOL ) print(f"DMRS positions: {dmrs_config.dmrs_positions}")# 生产DMRS plt.figure("DMRS Sequence", constrained_layout=True) i = 1for l in dmrs_config.dmrs_positions: num_dmrs = (N_SUBCARRIERS // 12) * 6 dmrs_seq = dmrs_config.generate_dmrs_sequence(num_dmrs, l) print(f'Dmrs Seq Generate sym {l} len { 2 * num_dmrs}: {dmrs_seq}') plt.subplot(2, 2, i) plt.scatter(np.real(np.array(dmrs_seq)), np.imag(np.array(dmrs_seq)), alpha=0.1) plt.title(f'Symbol {l}') plt.xlabel('I'); plt.ylabel('Q'); plt.grid(True); plt.axis('equal') i = i + 1 plt.show()