Lab1 帧同步实验
简介
在本实验,我们将实现基本的帧同步(frame synchronization)模块,并将通过Pluto设备进行发送和接收。通过实现该模块,对帧同步有一定的了解,同时也掌握如何使用Pluto硬件。
对于任何的MAC协议,参与无线通信的设备都需要进行帧同步,以发现正在传输的帧。实际传输过程中,会让发送机节点发送已知信号(preambles,通常称为“前导码”),而接收机节点将监听该信号。具体来说,前导码是在数据开始时发送前的波形,用于指示数据的开始。前导码波形是预先定义好的,因此不携带任何数据。接收机将侦听前导码,当检测到前导码后,接收机将开始解调数据包的其余部分。
首先,我们使用已经采集好的信号实现帧同步,采用课上提到的多种方式。然后,我们使用Pluto进行发送和接收,发送已知的信号,并对记录的信号执行帧同步。最后,我们将在Pluto上实现一个实时帧同步模块。
第一部分:实现帧同步检测函数(60%)
在这部分中,我们将探讨如何检测并同步到前导码。
检测和同步都可以使用相关操作来完成。两个离散信号x和y的相关结果是另一个离散信号,其定义为
$$ (x*y)[k] = \sum_{n=-∞}^{∞}x^\dagger[n] \cdot y[n+k] $$
换句话说,在每个索引k处,取第一个信号x的共轭,将第二个信号y偏移k个索引,然后将得到的两个信号相加。直观地说,相关性是衡量一个信号相对于另一个信号的相似度。在本课程中,我们会提到两种相关方法:自相关(auto-correlation)和互相关(cross-correlation)。虽然它们都使用相关的方法,但在他们的计算复杂度会有所不同。
此外,我们还提到了另外两种方法:能量检测和双滑动窗口检测。你需要实现这些方法。
问题1.1:使用互相关进行前导码检测(20%)
实现基于互相关的前导码检测器:
- 完善下面的detect_preamble_cross_correlation函数,通过下面的测试代码。该函数应接收两个信号,预先定义的preamble,需要做相关的信号signal。若找到前导码,则返回前导码开始位置的索引,若未找到,则返回None。
- 提交代码
# import settings
import math
import numpy as np
from matplotlib import pyplot
# Compare the correlation magnitude against this value to determine whether there is a preamble or not
def detect_preamble_cross_correlation(preamble, signal):
pass
# This cell will test your implementation of `detect_preamble`
preamble_length = 100
signal_length = 1000
preamble = (np.random.random(preamble_length) + 1j * np.random.random(preamble_length))
signalA = np.random.random(signal_length) + 1j * np.random.random(signal_length)
signalB = np.random.random(signal_length) + 1j * np.random.random(signal_length)
preamble_start_idx = 123
signalB[preamble_start_idx:preamble_start_idx + preamble_length] += preamble
np.testing.assert_equal(detect_preamble_cross_correlation(preamble, signalA), None)
np.testing.assert_equal(detect_preamble_cross_correlation(preamble, signalB), preamble_start_idx)
问题1.2:使用自相关进行前导码检测(20%)
实现基于自相关的前导码检测器(公式在课程中的ppt中已定义,可以使用迭代或递归公式来计算c[n]):
- 完善下面detect_preamble_auto_correlation函数,通过下面的测试代码。该函数应接收两个信号,需要做自相关的信号signal,信号中重复信号的长度short_preamble_len。若找到前导码,则返回前导码开始位置的索引,若未找到,则返回None。
- 提交代码
def detect_preamble_auto_correlation(signal, short_preamble_len):
pass
# This cell will test your implementation of `detect_preamble`
short_preamble_length = 20
signal_length = 1000
short_preamble = np.exp(2j * np.pi * np.random.random(short_preamble_length))
preamble = np.tile(short_preamble, 10) # 重复十次
noise = np.random.normal(size=signal_length) + 1j * np.random.normal(size=signal_length)
signalA = 0.1 * noise
signalB = 0.1 * noise
preamble_start_idx = 321
signalB[preamble_start_idx:preamble_start_idx + len(preamble)] += preamble
np.testing.assert_equal(detect_preamble_auto_correlation(preamble, signalA), None)
np.testing.assert_equal(detect_preamble_auto_correlation(preamble, signalB) in range(preamble_start_idx-5, preamble_start_idx+5), True)
问题1.3:使用能量检测进行前导码检测(10%)
实现基于能量检测的前导码检测器:
- 完善下面的detect_preamble_by_energy函数
- 提交代码
def detect_preamble_by_energy(signal):
pass
问题1.4:使用双滑动窗口的前导码检测(10%)
实现基于双滑动窗口的前导码检测器:
- 完善下面的detect_preamble_sliding函数
- 提交代码
def detect_preamble_by_sliding_window(signal, short_preamble_len):
pass
第二部分:使用预先采集的信号进行帧同步(20%)
有两个已事先记录好的信号:一个强信号和一个弱信号。每个信号文件中均有多个数据包。请增强上述函数以找出数据包开头的索引列表。
帧结构:|——10STS——|—2.5LTS—|———20 OFDM 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。
尝试使用preamble_lts或preamble_sts执行上述四种帧同步方法。
要求(提交报告)
- 使用能量检测方法执行同步:绘制能量图,计算SNR,并输出所有数据包起始索引。
- 使用双滑动窗口方法执行同步:绘制滑动计算结果图,并输出所有数据包开始索引。
- 使用互相关方法执行同步:绘制互相关结果,并输出所有数据包开头的索引。
- 使用自相关方法执行同步:绘制自相关结果,并输出所有数据包开头的索引。
- 请根据给定的弱信号和强信号的实验结果,对报告中的四种方法进行分析与总结。
preamble_lts = np.load("preamble_lts.npy")
preamble_sts = np.load("preamble_sts.npy")
received_signal_weak = np.load("recorded_signal_weak.npy")
received_signal_strong = np.load("recorded_signal_strong.npy")
第三部分:使用Pluto采集的信号进行帧同步(20%)
问题3.1:尝试使用Pluto设备记录信号(10%)
你可以使用以下程序记录信号。你需要将两个Pluto连接到你的电脑(或者分别连接到一台电脑上),使用下列IP: 192.168.2.1和IP: 192.168.3.2(或程序中的其他IP)。
注意:
- 需要实现设置好Pluto硬件的IP参数
- 你可以调整发射机中的tx_gain参数(或调整收发机的距离),以获得不同SNR的记录信号。
# Transmitter parameters configuration
from pluto_interface import pluto_transmitter
""" Parameters for PlutoSDR device """
tx_args = "ip:192.168.2.1"
tx_freq = 915e6
bandwidth = 1e6
tx_gain = -60
sdr_tx = pluto_transmitter(tx_args, tx_freq, bandwidth, tx_gain, verbose=True).pluto
# Get transmitted signal and transmit
transmitted_signal = np.load("tx_signal.npy")
transmitted_signal = transmitted_signal * (2 ** 14)
sdr_tx.tx_cyclic_buffer = True
sdr_tx.tx(transmitted_signal) # Cyclic transmit the signal
# Receiver parameters configuration
from 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 = 1e4
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("recorded_signal.npy", received_signal)
问题3.2:使用记录的信号重复第二部分实验 (10%)
要求
- 截图:显示已正确记录信号的命令行输出
- 报告:与问题2.1相同
第四部分:在线信号的帧同步(可选,加分:5%)
你需要快速处理在线信号,并打印数据包的起始索引。你可以再次使用第三部分中的发送机(但需要修改某些部分以显示你发送了多少数据包)。但你需要开发一个实时接收机,能够连续处理输入信号块。
要求
- 报告:如何设计与实现帧同步模块,截图显示实时发送和接收的数据包的数量。
- 提交代码