GNU Radio之Schmidl Cox OFDM synch.底层C++实现

文章目录

  • 前言
  • 一、Schmidl & Cox 同步模块
  • 二、C++ 源码分析
  • 三、处理流程
    • 1、延迟路径(Delay Path)
    • 2、能量路径(Energy Path)
    • 3、频率估计(Fine Frequency Estimate)
    • 4、峰值检测(Peak Detect)
    • 5、调试选项
  • 四、频率校正原理


前言

在 GNU Radio OFDM 系统中,一个非常重要的环节是在接收端准确地同步和检测发送端发出的信号。这就是 Schmidl & Cox 同步算法发挥作用的地方。Schmidl & Cox 算法是一种用于 OFDM 信号的时间同步的技术。本文对其底层 C++ 源码进行学习记录。


一、Schmidl & Cox 同步模块

在 GNU Radio 中,Schmidl & Cox 同步模块如下图所示,其接受三个参数,分别是:FFT 长度、循环前缀长度、检测阈值。
在这里插入图片描述
Schmidl & Cox 算法利用特殊设计的训练序列或前导符号(preamble),这些前导符号包含两个连续的相同部分,通常记为 A 和 A。这种结构使得接收端能够通过比较这两部分来估计开始接收数据的最佳时刻。

  • 相关性计算:通过计算连续两个 A 部分的相关性,可以检测到信号的起始点。如果两个部分完全一致,相关性达到最大值,这表明接收到的是预期的训练序列的开始部分。
  • 频偏估计:Schmidl & Cox 方法还可以用来估计和校正频率偏移。算法通过分析两个 A 部分的相位差异来估计频偏。

这里的训练序列或前导符号指的是同步字,在 ofdm grc 例程中用的是两个符号的同步字
在这里插入图片描述

  • 同步字 1 的内容为:[0., 0., 0., 0., 0., 0., 0., 1.41421356, 0., -1.41421356, 0., 1.41421356, 0., -1.41421356, 0., -1.41421356, 0., -1.41421356, 0., 1.41421356, 0., -1.41421356, 0., 1.41421356, 0., -1.41421356, 0., -1.41421356, 0., -1.41421356, 0., -1.41421356, 0., 1.41421356, 0., -1.41421356, 0., 1.41421356, 0., 1.41421356, 0., 1.41421356, 0., -1.41421356, 0., 1.41421356, 0., 1.41421356, 0., 1.41421356, 0., -1.41421356, 0., 1.41421356, 0., 1.41421356, 0., 1.41421356, 0., 0., 0., 0., 0., 0.]
    • 通过在偶数频率上传输伪噪声(PN)序列,使训练符号的两半(按时间顺序)相同,而在奇数频率上使用零
  • 同步字 2 的内容为:[0, 0, 0, 0, 0, 0, -1, -1, -1, -1, 1, 1, -1, -1, -1, 1, -1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, 1, -1, -1, 1, -1, 0, 1, -1, 1, 1, 1, -1, 1, 1, 1, -1, 1, 1, 1, 1, -1, 1, -1, -1, -1, 1, -1, 1, -1, -1, -1, -1, 0, 0, 0, 0, 0]
    • 第二个训练符号包含奇数频率上的 PN 序列来测量这些子信道,以及偶数频率上的另一个 PN 序列来帮助确定频率偏移

GNU Radio 的 Schmidl & Cox OFDM Sync 模块具有以下两个输出端:

  • 1、Correlation (或称为 Peak Detector) 输出:
    • 这个输出指示 OFDM 帧的起始位置。这个端口输出一个数字信号,用来明确指示第一个 OFDM 符号的开始。在这个信号中,当检测到一个 OFDM 帧的开始时,输出值为 1;在其他所有时间,输出值为 0。这种机制对于正确解码接收到的 OFDM 信号至关重要,因为它标定了 OFDM 符号的精确起点。
    • 通过使用特殊的训练序列来实现这种同步,这个训练序列在实际数据传输开始之前发送。该训练序列特别设计为两个连续的相同 OFDM 符号。这种设计允许算法在接收端通过比较这两个符号来实现同步:
      • ①、检测同步:
        • 互相关:算法计算两个连续训练符号之间的互相关。由于两个符号是相同的,它们之间的互相关应该在同一OFDM符号的起始位置达到最大值。
        • 能量比:算法还会计算两个训练符号的能量比率。这有助于确认互相关峰值是否真的表示一个符号的开始,还是仅仅因为噪声或其他信号干扰。
      • ②、标记开始:
        • 当算法确定了一个符号的开始位置后,它会在“detect”输出端口生成一个脉冲(值为1),表示第一个OFDM符号(即实际数据传输符号)的起始位置。在其他所有时间点,该端口的值为0。
  • 2、Frequency Offset (或称为 Fine Frequency Offset) 输出:
    • 这个输出提供了接收到的信号与发送信号之间的频率偏移估计。在 OFDM 系统中,由于发射机和接收机之间的本地振荡器可能不完全同步,频率偏移的估计和补偿是必要的。该输出提供了一个估计值,通常以一种可以直接用于频率校正模块的格式输出。
    • 该输出值表示归一化的频率偏移,计算方式如下:
      • F r e q u e n c y O f f s e t = 2.0 ∗ o u t p u t f f t _ l e n Frequency Offset = \frac{2.0*output}{fft\_len} FrequencyOffset=fft_len2.0output
      • 这里的 output0 是模块输出的原始频率偏移估计值,fft_len 是 FFT 的长度,归一化的频率偏移是指偏移量除以 FFT 长度的一半,原因是 FFT 的输出范围通常解释为从负一半到正一半(即 -0.5 到 0.5)的频率范围。
      • 归一化频率偏移的值告诉我们信号的载波频率与接收机本地振荡器之间的偏差,相对于总采样率的一半。
        • 当归一化频率偏移接近 0 时,表示频率偏移很小,信号质量好。
        • 当偏移值较大时,可能需要进行频率补偿以确保数据正确解码。

官方对其其输出也做了相应的解释:
在这里插入图片描述
其意思就是说:粗略频率偏移的评估不在该块中完成。此外,此处不计算初始均衡器抽头。
如果时序度量是 M ( d ) = ∣ P ( d ) ∣ 2 ( R ( d ) ) 2 M(d)=\frac{|P(d)|^2}{(R(d))^2} M(d)=(R(d))2P(d)2,那么归一化计算为 R ( d ) = 1 2 ∑ k = 0 N − 1 ∣ r k + d ∣ 2 R(d)=\frac{1}{2}\sum_{k=0}^{N-1}|r_{k+d}|^2 R(d)=21k=0N1rk+d2,(N = fft_len),这就意味着此模块估计两个半符号的能量,这可以避免在突发结束时能量水平突然下降时出现虚假检测。

他这里的理论参考的是 Robust Frequency and Timing Synchronization for OFDM. Timothy M. Schmidl and Donald C. Cox, Fellow, IEEE 的论文内容。

论文中指出:

  • 符号时序恢复依赖于在时域内搜索具有两个完全相同的训练符号,这两个训练符号在通过信道后将保持相同,只是它们之间会由于载波频率偏移而产生相位差。通过在偶数频率上传输伪噪声(PN)序列,使训练符号的两半(按时间顺序)相同,而在奇数频率上使用零。
  • 第二个训练符号包含奇数频率上的 PN 序列来测量这些子信道,以及偶数频率上的另一个 PN 序列来帮助确定频率偏移。
  • 设在第一个训练符号(不包括循环前缀)的一半中有 L 个复样本,并设乘积对的和为:
    • P ( d ) = ∑ m = 0 L − 1 r d + m ∗ r d + m + L ∗ P(d)=\sum_{m=0}^{L-1}r_{d+m}^{*}r_{d+m+L}^{*} P(d)=m=0L1rd+mrd+m+L
    • R ( d ) = ∑ m = 0 L − 1 ∣ r d + m + L ∣ 2 R(d)=\sum_{m=0}^{L-1}|r_{d+m+L}|^2 R(d)=m=0L1rd+m+L2
    • M ( d ) = ∣ P ( d ) ∣ 2 ( R ( d ) ) 2 M(d)=\frac{|P(d)|^2}{(R(d))^2} M(d)=(R(d))2P(d)2
      在这里插入图片描述

其实也就是说论文中使用的是估计半个符号的能量,而在 Schmidl & Cox 同步模块中,使用的是估计两个半符号(即一个完整的 OFDM 符号)的能量。

二、C++ 源码分析

ofdm_sync_sc_cfb_impl::ofdm_sync_sc_cfb_impl(int fft_len,				// FFT(快速傅立叶变换)的长度 int cp_len,				// 循环前缀的长度bool use_even_carriers,	// 一个布尔值,指示是否使用偶数承载波float threshold)			// 用于检测峰值的阈值: hier_block2("ofdm_sync_sc_cfb",io_signature::make(1, 1, sizeof(gr_complex)),
#ifndef SYNC_ADD_DEBUG_OUTPUTio_signature::make2(2, 2, sizeof(float), sizeof(unsigned char)))
#elseio_signature::make3(3, 3, sizeof(float), sizeof(unsigned char), sizeof(float)))
#endif
{
// 处理组件   ,代码创建了多个处理块,并且将这些块相互连接,形成数据处理的流程std::vector<float> ma_taps(fft_len / 2, 1.0);	// 被用作一个移动平均(Moving Average, MA)滤波器的系数gr::blocks::delay::sptr delay(	// 接收输入信号并将其延迟 fft_len / 2个样本。这用于创建与原始信号相比有一定时间偏移的信号gr::blocks::delay::make(sizeof(gr_complex), fft_len / 2));	gr::blocks::conjugate_cc::sptr delay_conjugate(gr::blocks::conjugate_cc::make());	// 取延迟信号的共轭,这在信号处理中用于某些相关计算gr::blocks::multiply_cc::sptr delay_corr(gr::blocks::multiply_cc::make());	// 将原始信号与延迟并共轭处理后的信号相乘,用于计算相关性,这是同步算法的一部分gr::filter::fir_filter_ccf::sptr delay_ma(gr::filter::fir_filter_ccf::make(	// 对上述乘积的结果应用滑动平均滤波器,滤波器的系数根据use_even_carriers确定为1或-11, std::vector<float>(fft_len / 2, use_even_carriers ? 1.0 : -1.0)));gr::blocks::complex_to_mag_squared::sptr delay_magsquare(	// 计算滤波后信号的幅度的平方,这有助于后续的峰值检测gr::blocks::complex_to_mag_squared::make());gr::blocks::divide_ff::sptr delay_normalize(gr::blocks::divide_ff::make());	// 将上述幅度平方与一个标准化信号相除,以调整不同条件下的信号级别gr::blocks::complex_to_mag_squared::sptr normalizer_magsquare(	// 用于计算复数输入信号的幅度的平方gr::blocks::complex_to_mag_squared::make());gr::filter::fir_filter_fff::sptr normalizer_ma(		// 创建一个FIR滤波器实例,它的滤波器系数是一个大小为 fft_len 的向量,每个元素值为gr::filter::fir_filter_fff::make(1, std::vector<float>(fft_len, 0.5)));gr::blocks::multiply_ff::sptr normalizer_square(gr::blocks::multiply_ff::make());	// 该乘法块用于将两个浮点数输入信号逐点相乘// 同步和频率估计gr::blocks::complex_to_arg::sptr peak_to_angle(gr::blocks::complex_to_arg::make());	// 将复数信号转换为其相角,用于频率误差的估计gr::blocks::sample_and_hold_ff::sptr sample_and_hold(	// 在检测到峰值时,保持当前的频率估计值,直到下一个峰值被检测到gr::blocks::sample_and_hold_ff::make());
// 峰值检测gr::blocks::plateau_detector_fb::sptr plateau_detector(	// 根据设置的阈值来检测峰值,这是确定信号同步点的关键步骤。gr::blocks::plateau_detector_fb::make(cp_len, threshold));// store plateau detector for use in callback setting thresholdd_plateau_detector = plateau_detector;// 连接组件,将所有这些组件按照处理逻辑连接起来。这包括将信号从一个块传输到另一个块,确保信号流能够按照设计的路径正确流动。// Delay Pathconnect(self(), 0, delay, 0);				// 连接源自身到延迟块connect(delay, 0, delay_conjugate, 0);		// 连接延迟块到共轭块connect(delay_conjugate, 0, delay_corr, 1);	// 连接共轭块到乘法块的第二个输入connect(self(), 0, delay_corr, 0);			// 连接源自身到乘法块的第一个输入connect(delay_corr, 0, delay_ma, 0);		// 连接乘法块到移动平均滤波器块connect(delay_ma, 0, delay_magsquare, 0);	// 连接移动平均滤波器块到幅度平方块connect(delay_magsquare, 0, delay_normalize, 0);	// 连接幅度平方块到归一化块// Energy Pathconnect(self(), 0, normalizer_magsquare, 0);	// 从源自身到能量平方块connect(normalizer_magsquare, 0, normalizer_ma, 0);	// 从能量平方块到移动平均滤波器connect(normalizer_ma, 0, normalizer_square, 0);	// 从移动平均滤波器到乘法块(第一个输入)connect(normalizer_ma, 0, normalizer_square, 1);	// 从移动平均滤波器到乘法块(第二个输入)connect(normalizer_square, 0, delay_normalize, 1);	// 从乘法块到归一化块// Fine frequency estimate (output 0)connect(delay_ma, 0, peak_to_angle, 0);	// 将delay_ma(移动平均滤波器)的输出连接到peak_to_angle块的输入connect(peak_to_angle, 0, sample_and_hold, 0);	// 从相位转换块到采样保持块connect(sample_and_hold, 0, self(), 0);		// 从采样保持块到层次块的输出// Peak detect (output 1)connect(delay_normalize, 0, plateau_detector, 0);	// 从归一化块到平台检测器connect(plateau_detector, 0, sample_and_hold, 1);	// 从平台检测器到采样保持块(第二输入)connect(plateau_detector, 0, self(), 1);	// 从平台检测器到层次块的第二输出
#ifdef SYNC_ADD_DEBUG_OUTPUT// Debugging: timing metric (output 2)connect(delay_normalize, 0, self(), 2);	// 将delay_normalize块的输出连接到层次块的第三个输出端口
#endif
}
  • ①、std::vector<float> ma_taps(fft_len / 2, 1.0);
    • 在 C++ 中用于创建一个类型为 float 的向量(vector),名称为 ma_taps。向量的大小是 fft_len / 2,其中每个元素的初始值都设置为 1.0。
    • 在信号处理中,这个向量 ma_taps 通常用作滤波器的系数。在给定的代码上下文中,这个向量被用作一个移动平均(Moving Average, MA)滤波器的系数。移动平均滤波器是一种常用的数字滤波器,主要用于平滑数据,减少噪声,从而提取有用的信号。
    • 移动平均滤波器的工作原理:
      • 系数设定:在这个案例中,所有系数都设为 1.0。这意味着每个输入样本对于输出的影响是均等的。
      • 滤波操作:移动平均滤波器通过对输入信号的连续样本进行平均来工作。具体来说,它计算当前和之前的 fft_len / 2 个样本的平均值来产生每一个输出样本。例如,如果 fft_len 是 1024,那么滤波器会计算最近 512 个样本的平均值。
    • 在 OFDM 同步中的应用:
      • 在 Schmidl & Cox OFDM 同步算法中,移动平均滤波器用于处理相关性输出。相关性计算是通过将接收到的信号与其延迟的共轭版本相乘来完成的这种乘积输出含有频谱的峰值,这些峰值可以揭示原始信号的起始位置
    • 使用移动平均滤波器来平滑这种乘积输出有助于:
      • 减少随机噪声:帮助稳定峰值检测,减少因噪声引起的错误检测。
      • 提升峰值的可识别性:通过平滑处理,使真正的峰值更加突出,而忽略较小的波动。
    • 因此,这个向量 ma_taps 虽然简单,但在整个 OFDM 同步过程中起着至关重要的作用,帮助算法准确地定位信号的起始点和同步帧。
  • ②、 gr::blocks::delay::sptr delay( gr::blocks::delay::make(sizeof(gr_complex), fft_len / 2));
    • 代码解析:
      • gr::blocks::delay::sptr delay:这部分定义了一个名为 delay 的智能指针(sptr,即shared pointer),指向 GNU Radio 中的一个延迟块。GNU Radio 通常使用智能指针来管理块的内存,这有助于自动处理块的生命周期和内存回收。
      • gr::blocks::delay::make(sizeof(gr_complex), fft_len / 2):这是一个静态工厂方法,用于创建延迟块的实例。它接收两个参数:
        • sizeof(gr_complex):这个参数指定了输入样本的大小,gr_complex表示复数类型的数据,sizeof(gr_complex)计算一个复数样本占用的字节数。这对于GNU Radio内部正确处理流中的数据非常重要。
        • fft_len / 2:这个参数指定延迟块应该引入的延迟量,以样本数为单位。在这里,它设置为FFT长度的一半。例如,如果FFT长度为1024,则延迟为512个样本。
    • 功能和用途
      • 在信号处理和尤其是OFDM系统中,延迟块有多种用途。在Schmidl & Cox OFDM同步算法的上下文中,延迟块的主要功能是为了创建一个与原始信号相比有时间偏移的信号版本。这种时间偏移的信号用于以下目的:
        • 相关计算:延迟块输出的信号会与原始信号的共轭相乘(通常在另一个处理块中完成)。这种乘积是计算信号自相关的一部分,自相关用于检测信号中的周期性模式,这对于确定OFDM符号的起始点非常关键。
        • 同步检测:通过比较原始信号和其延迟版本,可以检测到信号中的特定结构,如OFDM的循环前缀。这种检测是实现精确时间同步的基础。
  • ③、gr::blocks::divide_ff::sptr delay_normalize(gr::blocks::divide_ff::make());
    • 在Schmidl & Cox OFDM同步算法的上下文中,delay_normalize块用于以下具体步骤:
      • 归一化自相关结果:在计算信号的自相关后,通常得到的是原始幅度的平方,这些值可以非常大。通过将这些值除以信号的平均能量或某个参考值,可以将结果归一化,使其更加适合于后续的阈值检测和峰值检测。
      • 处理幅度平方信号:在OFDM同步中,除法块可能用于将由前面块(如complex_to_mag_squared)生成的幅度平方信号进行归一化,确保信号在不同接收条件下具有可比性。
  • ④、 connect(self(), 0, delay, 0); // 连接源自身到延迟块
    connect(delay, 0, delay_conjugate, 0); // 连接延迟块到共轭块
    connect(delay_conjugate, 0, delay_corr, 1); // 连接共轭块到乘法块的第二个输入
    connect(self(), 0, delay_corr, 0); // 连接源自身到乘法块的第一个输入
    connect(delay_corr, 0, delay_ma, 0); // 连接乘法块到移动平均滤波器块
    connect(delay_ma, 0, delay_magsquare, 0); // 连接移动平均滤波器块到幅度平方块
    connect(delay_magsquare, 0, delay_normalize, 0); // 连接幅度平方块到归一化块
    • 从接收信号开始,经过延迟、共轭、乘法处理、移动平均滤波,再到幅度平方和归一化处理。这个处理流程是为了实现精确的同步和信号特性提取,特别是在OFDM系统中,这些步骤对于准确地确定信号的起始点和进行频率估计是必需的。
  • ⑤、 connect(self(), 0, normalizer_magsquare, 0); // 从源自身到能量平方块
    connect(normalizer_magsquare, 0, normalizer_ma, 0); // 从能量平方块到移动平均滤波器
    connect(normalizer_ma, 0, normalizer_square, 0); // 从移动平均滤波器到乘法块(第一个输入)
    connect(normalizer_ma, 0, normalizer_square, 1); // 从移动平均滤波器到乘法块(第二个输入)
    connect(normalizer_square, 0, delay_normalize, 1); // 从乘法块到归一化块
    • 整个信号处理流程涉及信号能量的计算、平滑、再次加工(自乘)和用于最终归一化的准备。这些步骤共同构成了信号的能量路径,其目的是为信号的特性分析和同步处理提供稳定、可靠的能量信息
  • ⑥、connect(delay_ma, 0, peak_to_angle, 0); // 将delay_ma(移动平均滤波器)的输出连接到peak_to_angle块的输入
    connect(peak_to_angle, 0, sample_and_hold, 0); // 从相位转换块到采样保持块
    connect(sample_and_hold, 0, self(), 0); // 从采样保持块到层次块的输出
    • 整个流程是一个频率估计的实现,它从对信号的基本处理开始,通过提取相位信息,并在特定条件下锁定这些信息,最终提供一个稳定的频率估计输出。
  • ⑦、connect(delay_normalize, 0, plateau_detector, 0); // 从归一化块到平台检测器
    connect(plateau_detector, 0, sample_and_hold, 1); // 从平台检测器到采样保持块(第二输入)
    connect(plateau_detector, 0, self(), 1); // 从平台检测器到层次块的第二输出
    • 配置了OFDM同步过程中的关键功能:峰值检测,这对于准确同步接收的OFDM信号至关重要。通过检测信号的峰值来确定关键时刻(如OFDM符号的开始),并通过采样保持和输出确保这些信息能够被系统正确地利用。

三、处理流程

流程图如下:
在这里插入图片描述

处理流程包括多个信号处理块的创建和连接,涉及信号的延迟处理、能量分析、频率估计和峰值检测。下面将详细解释各个部分的功能和它们之间的关系。

1、延迟路径(Delay Path)

  • self()delay 块:
    • connect(self(), 0, delay, 0);
      • 这是信号处理流的开始,信号从层次化块的第一个输出端口流入 delay 块。delay 块用于对信号进行时间延迟,这是许多同步和信号处理算法的基本需求。
  • delaydelay_conjugate 块:
    • connect(delay, 0, delay_conjugate, 0);
      延迟后的信号进入 delay_conjugate 块,该块计算输入信号的复共轭。在信号处理中,复共轭常用于相关性计算和频谱分析。
  • delay_conjugatedelay_corr 块:
    • connect(delay_conjugate, 0, delay_corr, 1);
    • connect(self(), 0, delay_corr, 0);
      • 这里 delay_corr(一个复数乘法块)接收两个输入:直接来自 self() 的原始信号和来自 delay_conjugate 的延迟共轭信号。delay_corr 计算这两个信号的乘积,用于后续的相关性分析。
  • delay_corrdelay_ma 块:
    • connect(delay_corr, 0, delay_ma, 0);
      • 乘积输出进入 delay_ma(一个移动平均滤波器),用于平滑信号并减少噪声,这对于信号的稳定性和检测效果非常重要。
  • delay_madelay_magsquare 块:
    • connect(delay_ma, 0, delay_magsquare, 0);
      • 此块计算移动平均后信号的幅度平方,通常用于能量分析和信号强度评估。
  • delay_magsquaredelay_normalize 块:
    • connect(delay_magsquare, 0, delay_normalize, 0);
      • 最后,幅度平方信号被用来归一化,确保信号的幅度在一个标准化的范围内,为后续处理做好准备。

2、能量路径(Energy Path)

  • self()normalizer_magsquare 块:
    • connect(self(), 0, normalizer_magsquare, 0);
      • 直接将原始信号用于另一种能量分析,计算其幅度平方。
  • normalizer_magsquarenormalizer_ma 块:
    • connect(normalizer_magsquare, 0, normalizer_ma, 0);
      • 幅度平方信号进一步经过移动平均滤波处理,平滑信号。
  • normalizer_manormalizer_square 块:
    • connect(normalizer_ma, 0, normalizer_square, 0); connect(normalizer_ma, 0, normalizer_square, 1);
      • 这一步是将同一个平滑信号分两次输入到乘法块(normalizer_square),实际上是在计算信号的平方,进一步调整信号的动态范围。
  • normalizer_squaredelay_normalize 块:
    • connect(normalizer_square, 0, delay_normalize, 1);
      • 将处理后的能量信号与之前的延迟路径输出共同输入到归一化块,完成最终的信号归一化。

3、频率估计(Fine Frequency Estimate)

  • delay_mapeak_to_angle 块:
    • connect(delay_ma, 0, peak_to_angle, 0);
      • 将移动平均后的信号传输到相位转换块,用于提取信号的相位信息,这对于频率估计非常关键。
  • peak_to_anglesample_and_hold 块:
    • connect(peak_to_angle, 0, sample_and_hold, 0);
      • 相位信息被用于样本保持,确保频率估计在适当的时刻被锁定。
  • sample_and_holdself()
    • connect(sample_and_hold, 0, self(), 0);
      • 最终的频率估计结果被输出到层次化块的第一个输出端口。

4、峰值检测(Peak Detect)

  • delay_normalizeplateau_detector 块:
    • connect(delay_normalize, 0, plateau_detector, 0);
      • 归一化后的信号用于检测平台或峰值,这在同步信号时非常重要。
  • plateau_detectorsample_and_holdself()
    • connect(plateau_detector, 0, sample_and_hold, 1);
      connect(plateau_detector, 0, self(), 1);
      • 峰值检测结果用于第二个样本保持路径,并输出到层次化块的第二个输出端口。
        调试输出(Debugging: timing metric)

5、调试选项

  • delay_normalizeself()(如果启用调试):
    • connect(delay_normalize, 0, self(), 2);
      • 提供额外的调试信息输出,用于分析和调试信号处理流程。
        这段代码展示了一个复杂的信号处理框架,涉及多个处理阶段和功能块,其目的是实现精确的信号同步和特征提取,适用于高度动态的通信系统。

四、频率校正原理

这里将下图红框内的流程进行讲解梳理:
在这里插入图片描述
在这个GNU Radio流程图中,Schmidl & Cox OFDM synch 模块输出的频率偏移被送到了一个 Frequency Mod(频率调制)模块,然后此输出与延迟后的 OFDM 信号相乘。这是一种常见的频率校正方法,目的是要对接收到的 OFDM 信号进行补偿,以便修正由于接收机和发射机之间的频率偏差造成的效应。

流程说明:

  • 频率偏移检测:
    • Schmidl & Cox OFDM synch 模块的一个功能是估计接收信号的频率偏移量。该模块将频率偏移输出到 Frequency Mod 模块。
  • 频率调制:
    • Frequency Mod 模块根据输入的频率偏移量生成一个连续的复指数信号(相当于一个旋转向量),其频率与估计的偏移相对应。
  • 信号乘法(频率补偿):
    • 这个旋转向量与原始接收信号(通过 Delay 模块延迟一定时间以保持时序对齐)相乘,这实际上是将接收信号“旋转回去”以抵消频率偏移。换言之,如果接收信号的频率稍微高了一些,通过与一个频率稍微低的信号相乘,可以使得结果的频率与预期的 OFDM 子载波频率对齐。

为什么需要延迟?

  • 原始信号通过延迟模块的原因是为了使其与通过 Frequency Mod 模块生成的信号保持时序上的对齐。如果没有这个延迟,由于 Schmidl & Cox OFDM synch 模块和 Frequency Mod 模块本身的处理延迟,直接乘以原始信号将不会正确补偿频率偏移。

Robust Frequency and Timing Synchronization for OFDM 文献部分阅读笔记


我的qq:2442391036,欢迎交流!


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/652725.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Git泄露和hg泄露原理理解和题目实操

一.Git泄露 1.简介 Git是一个开源的分布式版本控制系统&#xff0c;它可以实现有效控制应用版本&#xff0c;但是在一旦在代码发布的时候&#xff0c;存在不规范的操作及配置&#xff0c;就很可能将源代码泄露出去。那么&#xff0c;一旦攻击者发现这个问题之后&#xff0c;就…

并并并并·病查坤

P1、什么是并查集 引用自百度百科&#xff1a; 并查集&#xff0c;在一些有N个元素的集合应用问题中&#xff0c;我们通常是在开始时让每个元素构成一个单元素的集合&#xff0c;然后按一定顺序将属于同一组的元素所在的集合合并&#xff0c;其间要反复查找一个元素在哪个集合…

MATLAB 2024a软件下载安装教程

1-首先下载Matlab&#xff0c;以下迅雷云链接&#xff0c;里面有全版本的matlab&#xff0c;根据自己的需要下载即可&#xff0c;建议下载最新版的&#xff0c;功能会更多&#xff0c;当然内存也会更大。 迅雷云盘迅雷云盘https://pan.xunlei.com/s/VNgH_6VFav8Kas-tRfxAb3XOA…

计算机体系结构:向量体系结构介绍

向量体系结构介绍 什么是向量&#xff1f; 在计算机体系结构&#xff0c;"向量"&#xff08;vector&#xff09;是指一个由多个相同类型且逻辑上相关的数据元素组成的有序集合。这些元素可以是整数、浮点数、布尔值或其他数据类型&#xff0c;它们在内存中连续存储…

GPT-3和DALL-E 2在AIGC领域具体有哪些应用?

GPT-3和DALL-E 2 是人工智能领域中两个备受关注的模型&#xff0c;它们分别代表了自然语言处理&#xff08;NLP&#xff09;和图像生成领域的最新进展。 1.GPT-3和DALL-E 2的概念与特点 1.1 GPT-3 GPT-3&#xff08;Generative Pre-trained Transformer 3&#xff09;&#x…

C语言 switch语句

之前 我们讲了 if 和 嵌套的if分支语句 但其实 多分支语句 我们还可以用 switch 有时 switch 语句可以简化逻辑代码 switch语句也称之为开关语句&#xff0c;其像多路开关一样&#xff0c;使程序控制流程形成多个分支&#xff0c;根据一个表达式的不同取值&#xff0c;选择其…

创建Vue3项目遇到的问题 - TypeError: (0 , import_node_util.parseArgs) is not a function

印象中想要创建vue3项目&#xff0c;需要安装16.0或更高版本的Node.js&#xff0c;于是第一步检查现在所用node版本。 显示 v16.20.0。前置条件符合&#xff0c;开始愉快的创建项目。npm init vuelatest&#xff0c;报错了。 查了一下&#xff0c;发现官网已经改成了需要18.3或…

k8s学习(三十六)centos下离线部署kubernetes1.30(单主节点)

文章目录 服务器准备工作一、升级操作系统内核1 查看操作系统和内核版本2 下载内核离线升级包3 升级内核4 确认内核版本 二、修改主机名/hosts文件1 修改主机名2 修改hosts文件 三、关闭防火墙四、关闭SELINUX配置五、时间同步1 下载NTP2 卸载3 安装4 配置4.1 主节点配置4.2 从…

Docker常见问题排查思路与实战

Docker作为一种流行的容器化技术&#xff0c;已经在众多场景中得到广泛应用。然而&#xff0c;在使用过程中&#xff0c;我们难免会遇到各种问题。本文将介绍一些常见的Docker问题及其排查思路&#xff0c;并通过实战案例帮助大家更好地理解和应对这些挑战。 1. Docker容器启动…

AWTK 开源串口屏开发(17) - 通过 MODBUS 访问数组数据

在 AWTK 串口屏中&#xff0c;内置了 MODBUS Client Channel 的模型&#xff0c;不用编写代码即可实现在 ListView 中显示数组数据。 MODBUS 协议一次只能读取 125 个 WORD&#xff0c;AWTK-MODBUS Client Channel 支持长数据&#xff0c;自动分成多个请求访问。 1. 功能 不用…

【Redis 开发】全局ID生成器

全局ID生成器 为了增加ID的安全性&#xff0c;我们可以不直接使用Redis自增的数值&#xff0c;而是拼接一些其他信息&#xff1a; ID的组成部分&#xff1a; 符号位:1bit,一直为0 时间戳:31bit&#xff0c;一秒为单位&#xff0c;可以使用69年 序列号:32bit&#xff0c;秒内…

编译工具各版本与操作系统版本号兼容性冷知识 : JetBrains IntelliJ IDEA 各个主要版本及其对应的操作系统版本号的兼容情况

编译工具各版本与操作系统版本号兼容性冷知识 &#x1f9e0;: JetBrains IntelliJ IDEA 各个主要版本及其对应的操作系统版本号的兼容情况 文章目录 编译工具各版本与操作系统版本号兼容性冷知识 &#x1f9e0;: JetBrains IntelliJ IDEA 各个主要版本及其对应的操作系统版本号…