前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >GNU Radio之OFDM Frame Equalizer底层C++实现

GNU Radio之OFDM Frame Equalizer底层C++实现

作者头像
Gnep@97
发布2024-05-01 08:45:35
1360
发布2024-05-01 08:45:35
举报
文章被收录于专栏:Gnep's_Technology_Blog

前言

OFDM Frame Equalizer 的功能是对标记的 OFDM 帧执行一维或二维均衡,本文对 OFDM Frame Equalizer 模块的底层 C++ 源码进行剖析。

一、OFDM Frame Equalizer 模块简介

  • 输入与输出:
    • 输入:一系列带标签的 OFDM 符号
    • 输出:与输入相同,但经过均衡和频率校正。
  • 参数:
    • FFT length:用于设置输入和输出向量大小
    • CP length:样本中循环前缀的长度(校正频率偏移所需)
    • Equalizer:将执行实际工作的均衡器对象
    • Length Tag Key:TSB密钥
    • Propagate Channel State:如果为 true,则最后一个符号之后的通道状态将作为标记添加到第一个符号
    • Fixed frame length: 设置帧长度是否固定。当给定此值时,长度标签键可以留空,但即使在输入处使用标记流时它也是有用的。
  • 实现原理
    • 首先,它会移除粗略的载波偏移。如果在第一个项目中找到带有 'ofdm_sync_carr_offset' 键的标签,这将被解释为以载波数量表示的粗略频率偏移。
    • 接下来,它在一个或两个维度上对标记的 OFDM 帧进行均衡。实际的均衡由一个名为 ofdm_frame_equalizer 的对象完成,该对象位于该块的外部。
    • 请注意,带有粗略载波偏移的标签没有被移除。该块下游的块不应尝试也去纠正这个偏移。

二、C++ 具体实现

1、初始化和配置参数

代码语言:javascript
复制
ofdm_frame_equalizer_vcvc_impl::ofdm_frame_equalizer_vcvc_impl(
    ofdm_equalizer_base::sptr equalizer,
    int cp_len,
    const std::string& tsb_key,		// 一个标签流块(Tagged Stream Block)的关键字,用于处理流中的数据包。
    bool propagate_channel_state,	// 决定是否传播通道状态
    int fixed_frame_len)			// 固定帧长,如果设置了固定帧长,则在处理时将使用这个长度
    : tagged_stream_block(
          "ofdm_frame_equalizer_vcvc",
          io_signature::make(1, 1, sizeof(gr_complex) * equalizer->fft_len()),
          io_signature::make(1, 1, sizeof(gr_complex) * equalizer->fft_len()),
          tsb_key),
      d_fft_len(equalizer->fft_len()),
      d_cp_len(cp_len),
      d_eq(equalizer),
      d_propagate_channel_state(propagate_channel_state),
      d_fixed_frame_len(fixed_frame_len),
      d_channel_state(equalizer->fft_len(), gr_complex(1, 0))
{
	// *************************错误处理*********************************
	/*
		这些行检查输入参数的有效性。如果既没有指定TSB标签也没有指定固定帧长,或者指定的帧长小于0,则抛出异常。
	*/
    if (tsb_key.empty() && fixed_frame_len == 0) {
        throw std::invalid_argument("Either specify a TSB tag or a fixed frame length!");
    }
    if (d_fixed_frame_len < 0) {
        throw std::invalid_argument("Invalid frame length!");
    }
	//  *************************设置输出倍数********************************* 
	/*
		如果设置了固定帧长,这行代码设置输出数据块的大小为固定帧长的倍数。
	*/
    if (d_fixed_frame_len) {
        set_output_multiple(d_fixed_frame_len);
    }
    set_relative_rate(1, 1);	// 设置输入和输出数据的相对速率,这里设置为1:1,表示输入和输出速率相同
    // Really, we have TPP_ONE_TO_ONE, but the channel state is not propagated
    set_tag_propagation_policy(TPP_DONT);	// 设置标签传播策略为不传播,这意味着这个块不会自动把输入的标签复制到输出。
}

2、解析出所需的输入项目数量

代码语言:javascript
复制
// 该函数的目的是从一组标签中解析出所需的输入项目数量
void ofdm_frame_equalizer_vcvc_impl::parse_length_tags(
    const std::vector<std::vector<tag_t>>& tags, gr_vector_int& n_input_items_reqd)	
    /*
     	tags: 每个 tag_t 代表一个数据流中的标签
     	n_input_items_reqd: 用于设置这个块处理每个数据流所需的项目(样本)数量
    */
    
{
    if (d_fixed_frame_len) {	// 如果设置了固定帧长度,就直接将n_input_items_reqd[0]设置为这个固定值
        n_input_items_reqd[0] = d_fixed_frame_len;
    } else {	// 如果没有固定的帧长度,代码会遍历tags[0]中的每个标签。(tags[0]表示与当前块相关的第一条数据流的所有标签。)
        for (unsigned k = 0; k < tags[0].size(); k++) {
            if (tags[0][k].key == pmt::string_to_symbol(d_length_tag_key_str)) {	// 如果标签的键与 d_length_tag_key_str(这是一个字符串,代表帧长度相关的标签键)相匹配
                n_input_items_reqd[0] = pmt::to_long(tags[0][k].value);	// 在当前处理周期内,该块需要读取的输入项目数
            }
        }
    }
}

3、处理 OFDM 信号的均衡

代码语言:javascript
复制
// 用于处理OFDM信号的均衡
int ofdm_frame_equalizer_vcvc_impl::work(int noutput_items,							// 指定输出项的数量
                                         gr_vector_int& ninput_items,				// 含有各个输入流的项数
                                         gr_vector_const_void_star& input_items,
                                         gr_vector_void_star& output_items)
{
    const gr_complex* in = (const gr_complex*)input_items[0];
    gr_complex* out = (gr_complex*)output_items[0];
    int carrier_offset = 0;		// 初始化载波偏移和帧长度变量
    int frame_len = 0;
    if (d_fixed_frame_len) {	// 如果有固定的帧长度,则使用该长度;否则使用输入项的数量作为帧长度
        frame_len = d_fixed_frame_len;
    } else {
        frame_len = ninput_items[0];
    }
	// *************************获取并处理标签*********************************
	/*
		从输入流中获取标签并处理。特别关注通道状态和载波偏移的标签。
	*/
    std::vector<tag_t> tags;
    get_tags_in_window(tags, 0, 0, 1);	// 从输入流的当前处理窗口中获取所有标签,并存储在 tags 向量中
    for (unsigned i = 0; i < tags.size(); i++) {	// 遍历标签,查找关键标签ofdm_sync_chan_taps和ofdm_sync_carr_offset,分别更新通道状态和载波偏移。
        if (pmt::symbol_to_string(tags[i].key) == "ofdm_sync_chan_taps") {
            d_channel_state = pmt::c32vector_elements(tags[i].value);
        }
        if (pmt::symbol_to_string(tags[i].key) == "ofdm_sync_carr_offset") {
            carrier_offset = pmt::to_long(tags[i].value);
        }
    }

    // Copy the frame and the channel state vector such that the symbols are shifted to
    // the correct position
    // 调整输入输出数据的对齐和拷贝
    /*
		根据载波偏移调整输入数据的位置,并将输入数据复制到输出缓冲区。
		如果载波偏移是负数,输出数组的开始部分将被清零,然后从输入数据中复制偏移后的数据。
		如果偏移是正数,则在数据复制后,输出数组的末尾部分被清零。
	*/
    if (carrier_offset < 0) {
        memset((void*)out, 0x00, sizeof(gr_complex) * (-carrier_offset));
        memcpy((void*)&out[-carrier_offset],
               (void*)in,
               sizeof(gr_complex) * (d_fft_len * frame_len + carrier_offset));
    } else {
        memset((void*)(out + d_fft_len * frame_len - carrier_offset),
               0x00,
               sizeof(gr_complex) * carrier_offset);
        memcpy((void*)out,
               (void*)(in + carrier_offset),
               sizeof(gr_complex) * (d_fft_len * frame_len - carrier_offset));
    }

    // Correct the frequency shift on the symbols
    // *************************频率偏移校正*********************************
    /*
		这部分代码通过乘以一个相位校正因子来补偿频率偏移。

		d_cp_len / d_fft_len 这两个参数的比例用于确定每个样本的相位偏差累积速率。
		具体来说,循环前缀的长度相对于FFT长度的比例影响了因频率偏移而导致的每个样本的相位变化
	*/
    gr_complex phase_correction;
    for (int i = 0; i < frame_len; i++) {
        phase_correction =
            gr_expj(-GR_M_TWOPI * carrier_offset * d_cp_len / d_fft_len * (i + 1));
        for (int k = 0; k < d_fft_len; k++) {
            out[i * d_fft_len + k] *= phase_correction;
        }
    }

    // Do the equalizing
    // 均衡处理
    d_eq->reset();		// 重置均衡器的状态
    d_eq->equalize(out, frame_len, d_channel_state);	// 调用均衡器来处理输出数据,根据当前的通道状态进行均衡。
    d_eq->get_channel_state(d_channel_state);			// 更新通道状态,以便在后续处理中使用

    // Update the channel state regarding the frequency offset
    phase_correction =
        gr_expj(GR_M_TWOPI * carrier_offset * d_cp_len / d_fft_len * frame_len);
    for (int k = 0; k < d_fft_len; k++) {
        d_channel_state[k] *= phase_correction;
    }

    // Propagate tags (except for the channel state and the TSB tag)
    // 传播标签(排除通道状态和 TSB 标签)
    get_tags_in_window(tags, 0, 0, frame_len);	// 从处理的数据流窗口中获取所有标签,并存储在tags向量中
    for (size_t i = 0; i < tags.size(); i++) {
        if (tags[i].key != CHAN_TAPS_KEY &&		// 如果标签的键不是通道状态(CHAN_TAPS_KEY)和不是固定帧长度关键字(d_length_tag_key_str),则将该标签添加到输出流的标签中。
            tags[i].key != pmt::mp(d_length_tag_key_str)) {
            add_item_tag(0, tags[i]);
        }
    }

    // Housekeeping
    // 状态维护
    if (d_propagate_channel_state) {	// 检查是否传播通道状态
        add_item_tag(0,
                     nitems_written(0),
                     pmt::string_to_symbol("ofdm_sync_chan_taps"),
                     pmt::init_c32vector(d_fft_len, d_channel_state));
    }

	// 处理固定帧长度
	/*
		consume_each 这个调用通知框架当前块已经处理并准备“消费”frame_len数量的输入项目。
		这是流处理中的标准操作,用于更新框架关于数据流进度的内部状态。
	*/
    if (d_fixed_frame_len && d_length_tag_key_str.empty()) {
        consume_each(frame_len);
    }

    return frame_len;
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-04-30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 一、OFDM Frame Equalizer 模块简介
  • 二、C++ 具体实现
    • 1、初始化和配置参数
      • 2、解析出所需的输入项目数量
        • 3、处理 OFDM 信号的均衡
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档