Lab2 物理层实验

注意事项

  • 截止日期:2023年5月1日23:59
  • 提交邮箱:ruicao@stu.xmu.edu.cn
  • 逾期提交每天会扣除5%的分数,最高扣35%

简介

本实验中,我们将开发一个OFDM接收机。在任何通信系统中,接收机通常比发射机更复杂,因为接收机需要处理一些不同形式的信号失真。对于我们的窄带OFDM系统,显然也是如此。

首先,接收方不知道传输将在何时发生,因此存在时间模糊性。我们在实验1中看到了如何用前导码同步来解决这个问题。此外,发射机和接收机具有不同的本地振荡器。振荡器频率不可避免的误差将导致发射机和接收机之间的载波频率偏移(CFO),并且由于振荡器不同步,发射机和接收机之间将有相位偏移。我们需要用前导码估计出CFO并补偿相位。如果我们传输一个长包或高阶调制信号,则需要在数据符号中使用导频来跟踪残留相位偏移。结合用前导码估计出的初始信道,我们能够进行信道均衡,然后解调信号。这样才能完成一个完整的OFDM接收机。

在本实验,首先会给你一些预先采集好的OFDM信号。你的工作是帧同步、估计CFO、消除CFO引起的相位偏移、估计信道,然后解调接收到的信号。然后,你需要自己捕获一个信号,并重复前面的步骤。

基础

帧结构 (时域)

|---------10 STS---------|-----2.5 LTS----|------------ DATA symbols------------|

每个帧的前导码由10个短训练序列(Short Training Symbol,STS)和2.5个长训练序列(Long Training Symbol,LTS)组成。1个STS长度为16,第一部分一共160 samples;1个LTS长度为64,第二部分一共160 samples,包括32+64+64。

每个数据符号的子载波结构 (频域,一般采用正负频率来表示)

img

本实验会采用64点的FFT。对于FFT后的信号数据序列,可以认为对应的频率位置为[0, ..., f, -f, ..., -1]。此时可以通过fftshift循环移位数据,使得数据对应的频率位置为[-f, ..., 0, ..., f]。因此我们以上图的[-32:31]的子载波序号来进行具体数据排列描述。

根据上图,为了防止带外干扰,[-32:-27]与[27:31]的子载波填0不传输数据,0号子载波因为存在DC的干扰,一般也不传输数据;[-21 -7 7 21]四个子载波用于放置导频(pilot);剩下的子载波会用于传输数据,传输数据在子载波上的排列顺序为:[1:6 8:20 22:26 -26:-22 -20:-8 -6:-1]。一共48个数据子载波,4个导频子载波,12个空闲子载波。

一个OFDM发射机的例子在ofdm_tx.py给出。接收机可以仿照该例子用python写出。为了降低项目复杂度,本实验还会给一个matlab接收机的例子。

每个OFDM符号的解调都可以看作是调制的逆过程。一种典型的解调步骤如下:

  1. 使用同步来找到数据包的起始位置。
  2. 做CFO估计与补偿。
  3. 对每个OFDM符号执行FFT。
  4. 从每个包的前导码中提取信道状态信息。
  5. 对每个符号进行解调

其中第5步有不同的实现方式。后续的实验会让你尝试不同的实现。按照课程上的描述,既考虑residual CFO也考虑SFO的实现步骤如下:

  • 5.1 纠正信道(信道均衡)
  • 5.2 估计残留CFO和SFO
  • 5.3 估计每个子载波的累积相位
  • 5.4 补偿每个子载波的累积相位
  • 5.5 解调数据子载波

注意:matlab接收机例子是一种简单的实现,忽略了步骤5.2-5.4。在只有AWGN噪声的仿真信号中是可行的,但是面对SDR采集下来的真实信号,可能会存在问题。

第一部分:OFDM信号解调 (40%)

问题1.1:已知数据发送信号的解调 (10%)

首先使用实验1的TX基带信号进行解调 (发送基带文件:tx_signal.npy,原始数据比特文件:raw_data.npy用于调试)。这种信号不经过无线信道也没有任何失真。你可以简单地实现调制过程的逆过程来解调。

在这个过程中,你无需执行步骤1、步骤2、步骤5.2~5.4。

要求

  • 报告:命令行输出显示正确解调
  • 代码:提交开发的接收机程序(python)

问题1.2:已知数据接收信号的解调 (10%)

使用实验1的RX基带信号recorded_signal_strong.npy进行解调。一个文件中的所有数据包都是相同的。你可以提取一个包来进行解调。原始数据比特都在文件raw_data.npy中,同样提供给你方便调试。

在这个过程中,你需要实现上述完整的步骤。

要求

  • 报告:命令行输出显示正确解调
  • 代码:提交开发的接收机程序(python)

问题1.3:未知数据接收信号的解调 (20%)

解调给定的采集下来的 BPSK 信号 (即recorded_signal_10sym.npyrecorded_signal_100sym.npy)并提交解调后的比特。不同的信号有不同的符号长度,recorded_signal_10sym.npy有10个数据符号,recorded_signal_100sym.npy 有100个数据符号。

要求:

  • 数据:提交解调数据 (用 npy 格式)
  • 代码:提交开发的接收机程序(python)

第二部分:OFDM信号解调方法分析 (50%)

问题2.1:解调过程分析 (20%)

按照上述解调方法1,使用 recorded_signal_100sym.npy 绘制以下图形。

要求:

  • 绘制用长训练序列(LTS)提取的信道幅度。采用X轴为 [-N/2, N/2-1]正负频率的表示形式(下同)。解释你所观察到的信道(例如, 你是否观察到频率选择性衰落)。

    提示:可以使用 np.fft.fftshift 将FFT结果从索引 [0, N-1] 移到 [-N/2, N/2-1]。

  • 选择一个导频子载波,并绘制一个数据包内所有OFDM符号的相位变化。解释你所观察到的结果。
  • 选择第1个/第10个/第20个OFDM符号, 并在所选符号中使用导频绘制所有子载波相位的线性插值图。解释你所观察到的结果。
  • 绘制信道均衡后所有OFDM子载波的星座点(即步骤5.4执行之后)

    提示: 可以使用 plt.scatter 来绘制星座点。

问题2.2:不做STO引起的相位补偿的分析(5%)

使用 recorded_signal_100sym.npy 绘制以下图形。

要求:

  • 如果我们仅在步骤5.2中估计残留CFO,绘制信道均衡后所有OFDM子载波的星座点 (即,不需要进行线性回归,只需将所有导频相位的平均值作为符号中的累积相位即可)。解释你所观察到的现象。

问题2.3:不做相位补偿的分析 (10%)

要求:

  • 如果跳过步骤5.2-5.4,为recorded_signal_10sym.npy绘制信道均衡后所有OFDM子载波的星座点图。将结果与你执行了步骤5.2-5.4的星座图进行比较,并解释你观察到的结果。
  • 如果跳过步骤5.2-5.4, 为recorded_signal_100sym.npy绘制信道均衡后所有OFDM子载波的星座点图。将结果与你执行了步骤5.2-5.4的星座图进行比较,并解释你观察到的结果。
  • 回答问题: 如果你不执行相位跟踪,对我们的OFDM系统有什么影响?

问题2.4:不做CFO补偿与相位补偿的分析 (10%)

要求:

  • 如果跳过步骤2和5.2-5.4, 为 recorded_signal_10sym.npy绘制信道均衡后所有OFDM子载波的星座点图。将结果与问题2.3和3.1中的星座点进行比较,并解释你所观察到的结果。
  • 如果跳过步骤2和5.2-5.4, 为 recorded_signal_100sym.npy绘制信道均衡后所有OFDM子载波的星座点图。将结果与问题2.3和3.1中的星座点进行比较,并解释你所观察到的结果。

问题2.5:研究CP去除位置的影响 (5%)

CP去除的位置(以及FFT解调窗口位置)可能会影响解调结果。你可以选择两个位置去除CP,比较估计的信道和解调结果。

要求:

  • 对于recorded_signal_10sym.npy,选择两个位置去除CP,放上屏幕截图显示使用两种CP去除位置均可以解调成功。
  • 比较两种CP去除位置下的信道估计结果,画出两个信道的振幅和相位,画出两个信道的相位差,并解释你所观察到的结果。

第三部分:采集一个信号并对其进行解调 (10%)

你需要使用示例发射机发送一个包,并使用Pluto采集基带信号并解调它。选取一个比较高SNR的信号,可以通过调整发射机的功率(即设置 tx_gain)或调整距离以产生高SNR。

发送机可以采用如下代码进行参数配置,注意OfdmTx是在ofdm_tx.py实现:

from ofdm.pluto_interface import pluto_transmitter
from ofdm.ofdm_tx import OfdmTx

# Transmitter parameters configuration  
""" Parameters for PlutoSDR device """
tx_args = "ip:192.168.2.1"  
tx_freq = 915e6  
bandwidth = 1e6  
tx_gain = -20  
sdr_tx = pluto_transmitter(tx_args, tx_freq, bandwidth, tx_gain, verbose=True).pluto  
# Receiver parameters configuration  

# Parameters for OFDM PHY  
n = 64  # Number of bits within every OFDM symbol  
cp = 16  # length of cyclic prefix within every OFDM symbol  
qam_size = 2  
pilot_pattern = 'custom'  
preamble_type = '802.11'  
num_symbol = 100  
ofdm_transmitter = OfdmTx(tx_args, tx_freq, bandwidth, tx_gain, n, cp, qam_size, pilot_pattern, preamble_type, num_symbol, verbose=True)  

以下是从随机包连续传输生成的基带信号并采集信号的示例代码。

# Receive the signal and record  
received_signal = sdr_rx.rx() # Record a buffer size signal one time  
np.save("recorded_signal.npy", received_signal) 

# Transmit the signal  
raw_data = np.random.randint(low=0, high=2, size=4800)  
OFDM_packet = ofdm_transmitter.process(raw_data) # Generate transmitted signal  
transmitted_signal = OFDM_packet * (2 ** 14)  
sdr_tx.tx_cyclic_buffer = True  
sdr_tx.tx(transmitted_signal) # Cyclic transmit the signal  

如果你要周期性的发送数据包,你需要将 tx_cyclic_buffer 设置为false, 并使用 for 循环发送数据包(可能需要一些休眠时间来减慢传输速率)。

sdr_tx.tx_cyclic_buffer = False
for packet_num in range(0, 1000):
  sdr_tx.tx(transmitted_signal)
  # time.sleep(0.01)
  print("Tx num:", packet_num)

接收机可以采用如下代码进行参数配置:

from ofdm.pluto_interface import pluto_receiver

""" Parameters for PlutoSDR device """  
rx_args = "ip:192.168.3.2"  
rx_freq = 915e6  
bandwidth = 1e6  
rx_gain = 0  
rx_buffer_size = 1e5  
gain_control_mode = "fast_attack"  
sdr_rx = pluto_receiver(rx_args, rx_freq, bandwidth, rx_gain, rx_buffer_size, gain_control_mode, verbose=True).pluto  

# Receive the signal and record
received_signal = sdr_rx.rx()  # Record a buffer size signal one time
np.save("received_signal.npy", received_signal)

要求:

  • 数据包长度是 4800 比特,对应多少个OFDM符号,是否与调制方式有关?raw_data大小和OFDM_packet大小有什么关系?
  • 截图:屏幕截图显示你的解调数据与发送的raw_data相匹配。
  • 给出信号的SNR值,绘制OFDM解调星座点。

第四部分:发送QPSK调制信号并解调 (可选,加分:5%)

在相同的数据符号长度下,高阶调制可以传输更多的数据位,但对解调误差更敏感。 在这个问题中,你尝试解调QPSK信号。BPSK 信号和 QPSK 信号之间的唯一区别是在子载波调制/解调步骤上(即步骤 5.5)。你需要传输一个包含100个数据符号的长数据包(即9600 比特)。

要求:

  • 绘制OFDM解调星座点。
  • 将解调后的数据与raw_data进行对比来检查你是否解调正确。放上已成功解调QPSK的屏幕截图。

提示:

  • 可以改变发送机程序中的qam_size = 4size = 9600 来产生 QPSK信号。
  • 请注意,如果采用上述发送机,QPSK是使用格雷码来调制每个子载波中的两个比特,接收机需要遵循同样的映射规则才能正确解调。

第五部分:实现实时在线解调 (可选,无加分,大项目要求)

你需要实现一个至少有20个数据符号的在线解调器。

要求:

  • 在实验1的帧同步实时接收机基础上继续开发解调实时接收机。
  • 截图显示你已经传输了多少个数据包,传输的比特,你已经解调了多少数据包以及解调的比特。