一个简化的发送端基带信号处理流程大概是下面这样的。
(在IFFT前后应该分别有串并转换和并串转换操作)
这里的Modulation部分一般就都是通过IQ调制完成的。
IQ调制是一种调制方式,通过这种方式可以实现PSK,QAM等各种调制方式。在通信系统中,对于需要发送的数据,首先需要按照对应的调制方式将输入数据分解到IQ分支上。
比如,如果使用的是16QAM,它的星座图是这样的:
那么输入数据就会4个一组,按照星座图的方式分别映射到I轴和Q轴。因为IQ默认是90度关系,所以只要知道I轴和Q轴的值就能够复原表示原来4位数据的向量,也就能知道原来的4位原始数据。原始数据与IQ值的对应关系如下:
把生成的IQ信号表示成复数的形式:I+jQ,然后再取N个复数做IFFT,对IFFT的结果取实部就得到一个OFDM符号在时域的采样点。其中N是当前带宽可用的子载波数。
可以这么理解,对于单个子载波,IQ调制的过程是这样的:
其中a,b就是单个子载波对应的IQ信号。
而对于OFDM符号,它在时域上是多个频率信号的叠加,它的公式如下:
x ( t ) = ∑ k = − N / 2 N / 2 − 1 X [ k ] e j 2 π k t / N x(t)=\sum_{k=-N/2}^{N/2-1}X[k]e^{j2\pi kt/N} x(t)=k=−N/2∑N/2−1X[k]ej2πkt/N
其中X[k]就相当于上面单载波中的IQ信号a+jb.
设IFFT的size为M,那么IFFT输出的M个时域离散值就是一个完整的OFDM符号时域所有的采样点。利用这些采样点就可以生成时域的OFDM基带信号。
在实际的通信系统中,IFFT的大小都是2的n次方,而子载波数却不一定是。因此通常M>N,那么在做IFFT的时候,是通过在N个IQ复数的基础上补零来完成M的IFFT。(待求证)
对于补零后的频域数据,在做IFFT之前通常会做循环移位,从而把零频分量移动到IFFT输入的中间。
在38.211中,PSS序列被定义为一个m序列。m序列是一类特殊的LFSR序列。它的生成结构如下:
对应的生成公式是这样的:
然后是PSS所占用的时频资源的位置:
以上这些都是生成PSS所需要的参数。
下面我们来看OAI中生成PSS的代码。
首先是函数定义及用到的变量的定义:
/*******************************************************************
*
* NAME : generate_pss_nr
*
* PARAMETERS : N_ID_2 : element 2 of physical layer cell identity
* value : { 0, 1, 2}
*
* RETURN : generate binary pss sequence (this is a m-sequence)
*
* DESCRIPTION : 3GPP TS 38.211 7.4.2.2 Primary synchronisation signal
* Sequence generation
*
*********************************************************************/
void generate_pss_nr(NR_DL_FRAME_PARMS *fp,int N_ID_2)
{
AssertFatal(fp->ofdm_symbol_size > 127,"Illegal ofdm_symbol_size %d\n",fp->ofdm_symbol_size);
AssertFatal(N_ID_2>=0 && N_ID_2 <=2,"Illegal N_ID_2 %d\n",N_ID_2);
int16_t d_pss[LENGTH_PSS_NR];
int16_t x[LENGTH_PSS_NR];
int16_t *primary_synchro_time = primary_synchro_time_nr[N_ID_2]; //存储三个PSS序列的数组
unsigned int length = fp->ofdm_symbol_size; //即IFFT size
unsigned int size = length * IQ_SIZE; /* i & q */
int16_t *primary_synchro = primary_synchro_nr[N_ID_2]; /* pss in complex with alternatively i then q */
int16_t *primary_synchro2 = primary_synchro_nr2[N_ID_2]; /* pss in complex with alternatively i then q */
void (*idft)(int16_t *,int16_t *, int);
初始化m序列的初始状态:
//38.211中,m序列的初始状态
#define INITIAL_PSS_NR (7)
const int x_initial[INITIAL_PSS_NR] = {0, 1, 1 , 0, 1, 1, 1};
然后按照协议公式计算出PSS序列中的127个值:
//按照38.211的公式计算127位序列
for (int i=0; i < INITIAL_PSS_NR; i++) {
x[i] = x_initial[i];
}
for (int i=0; i < (LENGTH_PSS_NR - INITIAL_PSS_NR); i++) {
x[i+INITIAL_PSS_NR] = (x[i + 4] + x[i])%(2);
}
for (int n=0; n < LENGTH_PSS_NR; n++) {
int m = (n + 43*N_ID_2)%(LENGTH_PSS_NR);
d_pss[n] = 1 - 2*x[m]; //这里相当于对PSS序列进行来BPSK的调制,由0,1序列调制为-1, 1
}
根据生成的原始PSS序列生成对应的IQ数据:
/* PSS is directly mapped to subcarrier without modulation 38.211 */
for (int i=0; i < LENGTH_PSS_NR; i++) {
#if 1
primary_synchro[2*i] = (d_pss[i] * SHRT_MAX)>>SCALING_PSS_NR; /* Maximum value for type short int ie int16_t */
primary_synchro[2*i+1] = 0; //按照实部虚部(I值Q值)的顺序以此存储IQ调制的结果
primary_synchro2[i] = d_pss[i];
#else
primary_synchro[2*i] = d_pss[i] * AMP;
primary_synchro[2*i+1] = 0;
primary_synchro2[i] = d_pss[i];
#endif
}
根据输入输出的周期性,将IFFT的输入数据进行循环移位,将零频分量移动到中间位置。这正是由于我们之前说的FFT size比实际输入数据大造成的。也就是说时域的OFDM一个符号的采样点数比实际可用子载波数要大。
/* call of IDFT should be done with ordered input as below
*
* n input samples
* <------------------------------------------------>
* 0 n
* are written into input buffer for IFFT
* -------------------------------------------------
* |xxxxxxx N/2 xxxxxxxx|
* --------------------------------------------------
* ^ ^ ^ ^ ^
* | | | | |
* n/2 end of n=0 start of n/2-1
* pss pss
*
* Frequencies
* positives negatives
* 0 (+N/2)(-N/2)
* |-----------------------><-------------------------|
*
* sample 0 is for continuous frequency which is used here
*/
//k就是上面图中start of pss的位置
unsigned int k = fp->first_carrier_offset + fp->ssb_start_subcarrier + 56; //and
if (k>= fp->ofdm_symbol_size) k-=fp->ofdm_symbol_size;
for (int i=0; i < LENGTH_PSS_NR; i++) {
synchroF_tmp[2*k] = primary_synchro[2*i];
synchroF_tmp[2*k+1] = primary_synchro[2*i+1];
k++;
if (k == length) k=0;
}
最后通过IFFT生成时域序列,注意这里时域的序列也是按照实部虚部的顺序存储的。
/* IFFT will give temporal signal of Pss */
idft = get_idft(length);
idft(synchroF_tmp, /* complex input */
synchro_tmp, /* complex output */
1); /* scaling factor */
/* then get final pss in time */
for (unsigned int i=0; i<length; i++) {
((int32_t *)primary_synchro_time)[i] = ((int32_t *)synchro_tmp)[i];
}
参考资料:
关于IQ调制的详细内容可以参考:
https://www.mobibrw.com/2018/12118
关于通信系统的完整流程可以参考:
https://zhuanlan.zhihu.com/p/25868603
关于OFDM调制可以参考:
https://www.csie.ntu.edu.tw/~hsinmu/courses/_media/wn_11fall/ofdm_new.pdf
https://zhuanlan.zhihu.com/p/57967971
https://blog.csdn.net/zzsfqiuyigui/article/details/9091363
关于5G PSS定义可以参考:
http://www.techplayon.com/nr-primary-synchronization-signal-pss/
http://www.dpi-proceedings.com/index.php/dtcse/article/viewFile/26293/25707
http://www.rfwireless-world.com/5G/5G-NR-m-sequence.html