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

GNU Radio之OFDM Serializer底层C++实现

作者头像
Gnep@97
发布2024-05-01 08:46:42
650
发布2024-05-01 08:46:42
举报

前言

GNU Radio 中 OFDM Serializer 模块是 OFDM Carrier Allocator 逆块,其功能为将 OFDM 子载波的复杂调制符号序列化(并串转换模块),输出复数数据符号作为一个带标签的流,并丢弃导频符号。

一、OFDM Serializer 简介

在这里插入图片描述
在这里插入图片描述
  • 输入与输出
    • 输入:复数长度向量
    • 输出:复数标量,其顺序与占用的载波中指定的顺序相同。
  • 参数
    • FFT length:FFT 长度
    • Occupied Carriers:占据的子载波
    • Length Tag Key:标识输入帧长度(以OFDM符号计)的标签键
    • Packet Length Tag Key:标识此数据包中复数符号数量的标签键
    • Symbols skipped:如果第一个符号没有按照 occupied_carriers[0] 的分配设置,请设置这个参数
    • Carrier Offset Key:当这个块需要纠正载波偏移时,在这里指定偏移的标签键(如果后面接的是OFDM帧均衡器则不必指定)。
    • Input is shifted:如果输入在索引 0 上具有 DC 载波(即未进行 FFT 移位),则将其设置为 false
  • 实现原理
    • OFDM Serializer 是 OFDM 载波分配器的逆向块。它输出复数数据符号作为一个带标签的流,并丢弃导频符号。
    • 如果提供,将解析两个不同的标签:第一个键(长度标签键)指定输入帧中的 OFDM 符号数量。第二个键(数据包长度标签键)指定编码到该帧中的复数符号数量。如果提供了这第二个键,则在输出时使用;否则,使用长度标签键。如果两者都提供,数据包长度指定输出项的最大数量,而帧长度指定消耗的输入项的确切数量。
    • 通过传递带有该偏移的另一个标签,可以在此功能中纠正载波偏移。

二、C++ 具体实现

1、初始化和配置参数

代码语言:javascript
复制
// 构造函数, 包含了对输入信号的参数设置、检查和初始化步骤
ofdm_serializer_vcc_impl::ofdm_serializer_vcc_impl(
    int fft_len,												// FFT处理的长度
    const std::vector<std::vector<int>>& occupied_carriers,		// 指定在FFT带宽中哪些频率载波是被占用的
    const std::string& len_tag_key,								// 用于标签流处理,标识长度的键
    const std::string& packet_len_tag_key,						// 用于标签流处理,标识数据包长度的键
    int symbols_skipped,										// 开始处理前应跳过的符号数
    const std::string& carr_offset_key,							// 用于载波偏移的标签键
    bool input_is_shifted)										// 指示输入数据是否已经进行了频率移位
    : tagged_stream_block("ofdm_serializer_vcc",
                          io_signature::make(1, 1, sizeof(gr_complex) * fft_len),	// io_signature 定义了输入和输出的数据类型和大小。
                          io_signature::make(1, 1, sizeof(gr_complex)),
                          len_tag_key),
      d_fft_len(fft_len),										// FFT长度
      d_occupied_carriers(occupied_carriers),					// 存储占用的载波信息
      d_packet_len_tag_key(pmt::string_to_symbol(packet_len_tag_key)),
      d_out_len_tag_key(pmt::string_to_symbol(
          (packet_len_tag_key.empty() ? len_tag_key : packet_len_tag_key))),
      d_symbols_skipped(symbols_skipped % occupied_carriers.size()),
      d_carr_offset_key(pmt::string_to_symbol(carr_offset_key)),
      d_curr_set(symbols_skipped % occupied_carriers.size()),
      d_symbols_per_set(0)	// 记录在一个OFDM符号集中,所有被占用的载波上分配的数据和导频符号的总数;"集"指的是一个OFDM帧
{
	// *************************载波占用和频率移位处理**************************
	/*
		这段循环通过对每个载波的索引进行修改来处理频率移位(如果input_is_shifted为真)。
		对于每个载波,根据FFT长度进行调整,确保它们都在有效范围内(64 个子载波编号为 [-32,31],
		将其变为 [0, 63])。如果载波索引超出了FFT长度的范围,会抛出异常。
	*/
    for (unsigned i = 0; i < d_occupied_carriers.size(); i++) {
        for (unsigned k = 0; k < d_occupied_carriers[i].size(); k++) {
            if (input_is_shifted) {
                d_occupied_carriers[i][k] += fft_len / 2;
                if (d_occupied_carriers[i][k] > fft_len) {
                    d_occupied_carriers[i][k] -= fft_len;
                }
            } else {
                if (d_occupied_carriers[i][k] < 0) {
                    d_occupied_carriers[i][k] += fft_len;
                }
            }
            if (d_occupied_carriers[i][k] >= fft_len || d_occupied_carriers[i][k] < 0) {
                throw std::invalid_argument("ofdm_serializer_vcc: trying to occupy a "
                                            "carrier outside the fft length.");
            }
        }
    }

	// 计算每个集合的符号数
    for (unsigned i = 0; i < d_occupied_carriers.size(); i++) {
        d_symbols_per_set += d_occupied_carriers[i].size();	// 计算所有占用载波集合中的符号总数,用于设置处理速率。
    }
    set_relative_rate((uint64_t)d_symbols_per_set, (uint64_t)d_occupied_carriers.size());
    set_tag_propagation_policy(TPP_DONT);	// 设置标签传播政策为不传播,意味着这个块不会自动将输入标签复制到输出标签
}

2、计算输出流长度

代码语言:javascript
复制
int ofdm_serializer_vcc_impl::calculate_output_stream_length(
    const gr_vector_int& ninput_items)	// 包含了每个输入流中可用的样本数。在这个上下文中,向量中的第一个元素(ninput_items[0])表示当前块处理的输入流中的样本数。
{
	/*
		ninput_items[0] / d_occupied_carriers.size():这部分计算整个输入样本可以完全覆盖多少个完整的载波集合。

		将完整覆盖的载波集数乘以每个集合中的符号数(d_symbols_per_set),得到基本的输出样本数。
	*/
	int nout = (ninput_items[0] / d_occupied_carriers.size()) * d_symbols_per_set;	// 用于计算输出流的长度

	// 处理余数部分
	/*
		这部分计算每个额外处理的载波集中有多少个符号,然后加到nout上
	*/
	for (unsigned i = 0; i < ninput_items[0] % d_occupied_carriers.size(); i++) {
		
        nout += d_occupied_carriers[(i + d_curr_set) % d_occupied_carriers.size()].size();
    }
    return nout;
}

3、更新流标签

代码语言:javascript
复制
void ofdm_serializer_vcc_impl::update_length_tags(int n_produced, int n_ports)
{
	// 更新流标签,用于指示输出流中的数据长度
    add_item_tag(0, nitems_written(0), d_out_len_tag_key, pmt::from_long(n_produced));
}

4、OFDM 信号的序列化(并串转换)

代码语言:javascript
复制
// 处理 OFDM 信号的序列化
int ofdm_serializer_vcc_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];				// 各输入流中的项数列表
    long frame_length = ninput_items[0]; // Input frame		记录输入帧的长度
    long packet_length = 0;              // Output frame	用于记录输出帧的长度
    int carr_offset = 0;

    std::vector<tag_t> tags;
    // Packet mode
    if (!d_length_tag_key_str.empty()) {
        get_tags_in_range(tags, 0, nitems_read(0), nitems_read(0) + 1);	// 检索当前处理范围内的标签
        for (unsigned i = 0; i < tags.size(); i++) {	// 载波偏移
            if (tags[i].key == d_carr_offset_key) {
                carr_offset = pmt::to_long(tags[i].value);
            }
            if (tags[i].key == d_packet_len_tag_key) {	// 包长度的信息
                packet_length = pmt::to_long(tags[i].value);
            }
        }
    } else {
        // recalc frame length from noutput_items
        frame_length = 0;
        int sym_per_frame = 0;
        while ((sym_per_frame +
                d_occupied_carriers[(frame_length + 1) % d_occupied_carriers.size()]
                    .size()) < (size_t)noutput_items) {
            frame_length++;
            sym_per_frame +=
                d_occupied_carriers[(frame_length + 1) % d_occupied_carriers.size()]
                    .size();
        }
    }

    // Copy symbols
    // *************************符号复制和标签处理*************************
    /*
		在处理每个输入符号时,将关联的标签(除了包长度标签)复制到输出符号上。
		然后,根据载波偏移和当前的载波集设置,从输入复制符号到输出。
	*/
    int n_out_symbols = 0;
    for (int i = 0; i < frame_length; i++) {
        // Copy all tags associated with this input OFDM symbol onto the first output
        // symbol
        get_tags_in_range(tags, 0, nitems_read(0) + i, nitems_read(0) + i + 1);
        for (size_t t = 0; t < tags.size(); t++) {
            // The packet length tag is not propagated
            if (tags[t].key != d_packet_len_tag_key) {
                add_item_tag(
                    0, nitems_written(0) + n_out_symbols, tags[t].key, tags[t].value);
            }
        }
        for (unsigned k = 0; k < d_occupied_carriers[d_curr_set].size(); k++) {
            out[n_out_symbols++] =
                in[i * d_fft_len + d_occupied_carriers[d_curr_set][k] + carr_offset];
        }
		
		// 包长度检查
		/*
			如果设置了包长度,并且输出符号数超过了这个长度,就将输出符号数调整为包长度,
			并提前终止处理。
		*/
        if (packet_length && n_out_symbols > packet_length) {
            n_out_symbols = packet_length;
            break;
        }
        d_curr_set = (d_curr_set + 1) % d_occupied_carriers.size();
    }

    // Housekeeping
    // 结束处理
    /*
		消费输入项:如果没有长度标签键,根据计算的帧长度消费输入项。
					否则,重置当前的载波集设置,准备下一次调用。
	*/
    if (d_length_tag_key_str.empty()) {
        consume_each(frame_length);
    } else {
        d_curr_set = d_symbols_skipped;
    }

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

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

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

本文参与?腾讯云自媒体分享计划? ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 一、OFDM Serializer 简介
  • 二、C++ 具体实现
    • 1、初始化和配置参数
      • 2、计算输出流长度
        • 3、更新流标签
          • 4、OFDM 信号的序列化(并串转换)
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
          http://www.vxiaotou.com