OFDM技术的基本原理是将传输信道分解为若干个正交的子信道,将需要传输的高速数据信号转换为并行的低速数据流,并调制到每个正交信道的子载波上进行传输,叠加传输的若干正交信号在接收端通过FFT变换恢复出原始数据流。
OFDM技术的基本原理
直接上图,图片信息来自《MIMO-OFDM无线通信技术及matlab仿真》
FEC编码
FEC编码(不是本次的重点,感兴趣的同学可以看下我的这篇博客 LDPC的比特翻转算法与和积译码算法的python实现)是一种纠错编码,它的作用是在传输码列中加入冗余纠错码,在一定条件下,通过解码可以自动纠正传输误码,降低接收信号的误码率(BER)。FEC编码技术在远程通信、信息论、编码理论中被广泛应用,是在不可靠或强噪声干扰的信道中传输数据时用来控制错误的一项技术。FEC编码技术具有引入级联信道编码等增益编码技术的特点,可以自动纠正传输误码的优点。
虚载波和加窗
OFDM中虚载波(VC)的作用是为了保证子载波之间正交,避免子载波之间的干扰。在OFDM中,虚载波是一种不携带信息的载波,它的频率与实际携带信息的子载波频率相同,但是相位为0。虚载波的作用是在时域上形成一个周期为T的矩形函数,使得在频域上,各个子载波之间正交。
升余弦窗(RC)是一种窗函数,它的作用是在时域上对信号进行加窗,以减小信号在频域上的泄漏。升余弦窗是一种平滑的窗函数,它的主瓣宽度比矩形窗要小,旁瓣衰减得比较快,因此在频域上的泄漏比矩形窗要小。升余弦窗可以看作是三个矩形时间窗的频谱之和,或者说是三个 sinc (t) 型函数之和,而括号中的两项相对于第一个谱窗向左、右各移动了π/T,从而使旁瓣互相抵消,消去高频干扰和漏能。
将RC和VC一起使用可以减小带外功率,抑制邻道间干扰。
循环前缀(CP)
OFDM中的循环前缀是指在每个OFDM符号的开头添加一段与该符号结尾相同的信号,以形成一个循环结构。循环前缀的作用是在时域上对信号进行加窗,以减小信号在频域上的泄漏。循环前缀主要充当连续符号之间的保护带,以克服符号间干扰ISI。
导频符号
OFDM中的导频符号是指在OFDM符号中插入的一些特殊符号,用于信道估计和同步。导频符号可以安插在OFDM的时间和频率二维结构内,只要在两个方向的导频密度满足二维Nyquist定理,就可以精确估计信道的时变和衰落特性,因此能够适应快衰落信道。
STO/CFO和信道估计
本次仿真暂不涉及,如果大家感兴趣的话后续仿真加上这些功能。
本次仿真模拟以基本的OFDM概念进行仿真,加入了VC和CP。直接贴上代码,感兴趣的同学可以直接拷出来运行。
import numpy as np
# 16QAM星座点
constellation = np.array([-3-3j, -1-3j, 1-3j, 3-3j, -3-1j, -1-1j, 1-1j, 3-1j, -3+1j, -1+1j, 1+1j, 3+1j, -3+3j, -1+3j, 1+3j, 3+3j])
def qam16_modulation(bits):
# 将比特流分组为4个比特
bits_grouped = np.reshape(bits, (-1, 4))
# 将比特组转换为QAM星座点的索引
indices = np.packbits(bits_grouped, axis=1, bitorder='little').flatten()
# 获取对应的星座点
symbols = constellation[indices]
return symbols
def ofdm_transmitter(symbols, fft_size=64, cp_size=16, vc_size=16):
N_used = (fft_size-vc_size)
# 计算OFDM符号数
num_symbols = len(symbols) // N_used
# 将星座点重新分组为OFDM符号
symbols_grouped = np.reshape(symbols[:num_symbols*N_used], (num_symbols, N_used))
#添加VC
symbols_grouped_vc = np.concatenate((np.zeros((num_symbols, vc_size)), symbols_grouped), axis=1)
# 进行FFT变换
freq_symbols = np.fft.ifft(symbols_grouped_vc, axis=1)
# 添加循环前缀
freq_symbols_cp = np.concatenate((freq_symbols[:, -cp_size:], freq_symbols), axis=1)
# 将OFDM符号串联起来
time_signal = freq_symbols_cp.flatten()
return time_signal
def ofdm_receiver(time_signal, fft_size=64, cp_size=16, vc_size=16):
# 计算OFDM符号数
num_symbols = len(time_signal) // (fft_size + cp_size)
# 将时域信号分组为OFDM符号
time_symbols = np.reshape(time_signal[:num_symbols*(fft_size+cp_size)], (num_symbols, fft_size+cp_size))
#去除循环前缀
time_symbols_cp_removed = time_symbols[:, cp_size:]
# 进行FFT变换
freq_symbols = np.fft.fft(time_symbols_cp_removed, axis=1)
# 去除VC,将频域符号展开成一维数组
freq_symbols_flat = freq_symbols[:,16:].flatten()
# 获取星座点的索引
indices = np.argmin(np.abs(freq_symbols_flat[:, None] - constellation[None, :]), axis=1)
indices = np.uint8(indices)
# 将索引转换为比特
bits = np.unpackbits(indices, bitorder='little').reshape(-1, 8)[:, :4].flatten()
return bits
#生成随机比特流
bits = np.random.randint(0, 2, 192000)
#进行16QAM调制
symbols = qam16_modulation(bits) #2500
#进行OFDM传输
time_signal = ofdm_transmitter(symbols)
#添加噪声
noise = np.random.normal(0, 0.1, len(time_signal)) + 1j*np.random.normal(0, 0.1, len(time_signal))
received_signal = time_signal + noise
#进行OFDM接收
received_bits = ofdm_receiver(received_signal)
#计算误比特率
ber = np.mean(bits != received_bits)
print("误比特率:", ber)
结语
需要注意的是,OFDM系统的性能受到许多因素的影响,包括循环前缀长度、调制方式、信道估计等等。因此,在实际应用中,需要仔细调整这些参数以获得最佳的性能。
另外,需要注意的是,本示例中使用的是理想的OFDM系统,没有考虑多径信道和频率偏移等问题。在实际应用中,这些问题需要通过信道估计和均衡等技术来解决。
总之,OFDM是一种非常重要的调制技术,广泛应用于现代通信系统中。通过使用numpy等工具,我们可以轻松地实现OFDM系统并进行仿真分析,从而更好地理解和应用这种技术。