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

GNU Radio之OFDM Divide和Matrix Transpose底层C++实现

作者头像
Gnep@97
发布2024-05-26 15:49:42
930
发布2024-05-26 15:49:42
举报
文章被收录于专栏:Gnep's_Technology_Blog

前言

gr-radar 中的 OFDM Divide 模块是GNU Radio中的一个组件,专门用于处理正交频分复用(OFDM)信号。这个模块主要执行复数信号的除法操作,通常用于雷达和通信系统中的信号处理。

下面对这个模块进行介绍并详细分析其底层 C++ 代码实现。

一、OFDM Divide 模块

1、简介

2、模块作用

这个模块执行复杂的复数除法,用 in0/in1 进行计算。如果 vlen_out 大于 vlen_in,则额外的空间将填充为零。这可以用于零填充。

下面是引自一篇硕士论文(车联网背景下的雷达通信一体化感知方法研究与平台实现)中讲述矩阵相除的作用:

3、参数意义

  • Vector length input:这个参数定义了输入向量的长度。在 OFDM 系统中,这通常对应于快速傅里叶变换(FFT)的长度,代表了每个 OFDM 符号中的子载波数量。
  • Vector length output:这个用来设置输出向量的长度。
    • 这里的取值含义为它从 FFT 长度中减去被舍弃的载波数量(len(discarded_carriers)),然后乘以一个零填充因子(zeropadding_fac)。这个机制允许调整输出数据的大小,可以用于在信号处理后进行缩放或额外的零填充。
  • Discarded carriers:这个参数是一个集合,列出了需要在除法操作中被忽略的载波。通过设置这些载波为零,可以在后续处理中排除它们的影响。
  • Number of sync words:这个参数指定了有多少个同步字在处理时不应用舍弃载波的规则。同步字是用于帮助接收器定位和同步信号的特定数据序列。允许这些部分不受舍弃载波设置的影响,确保信号的同步和稳定性不会受到干扰。
  • Packet length key:这个参数用于指定在处理数据包时使用的关键字,它标识了数据包的长度。

4、C++ 具体实现

注释已标注清楚:

代码语言:javascript
复制
/* -*- c++ -*- */
/*
 * Copyright 2014 Communications Engineering Lab, KIT.
 *
 * This is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3, or (at your option)
 * any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this software; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street,
 * Boston, MA 02110-1301, USA.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "ofdm_divide_vcvc_impl.h"
#include <gnuradio/io_signature.h>

namespace gr {
namespace radar {

ofdm_divide_vcvc::sptr ofdm_divide_vcvc::make(int vlen_in,
                                              int vlen_out,
                                              std::vector<int> discarded_carriers,
                                              int num_sync_words,
                                              std::string len_key)
{
    return gnuradio::get_initial_sptr(new ofdm_divide_vcvc_impl(
        vlen_in, vlen_out, discarded_carriers, num_sync_words, len_key));
}

/*
 * The private constructor
 */
ofdm_divide_vcvc_impl::ofdm_divide_vcvc_impl(int vlen_in,			// 输入向量的长度
                                             int vlen_out,			// 输出向量的长度
                                             std::vector<int> discarded_carriers,	// 应该在处理中被忽略的载波索引列表
                                             int num_sync_words,	// 同步字的数量,这些字在处理时不受前述舍弃规则的影响
                                             std::string len_key)	// 用于指定数据包长度的标签键
    : gr::tagged_stream_block("ofdm_divide_vcvc",
                              gr::io_signature::make(2, 2, sizeof(gr_complex) * vlen_in),
                              gr::io_signature::make(1, 1, sizeof(gr_complex) * vlen_out),
                              len_key)
{
    d_vlen_in = vlen_in;		
    d_vlen_out = vlen_out;
    d_discarded_carriers = discarded_carriers;
    d_num_sync_words = num_sync_words;

    // Shift discarded carriers
    // 将每个舍弃载波的索引值加上输入向量长度的一半。这样的偏移是因为在某些OFDM实现中,频谱可能是以零频为中心的,
    // 所以需要调整舍弃载波的位置以正确对应到频谱的负频和正频部分
    for (int k = 0; k < discarded_carriers.size(); k++) {
        d_discarded_carriers[k] = discarded_carriers[k] + vlen_in / 2;
    }

    // Error handling
    // 错误处理机制,如果输出向量的长度小于输入向量的长度,则抛出异常
    if (d_vlen_out < d_vlen_in)
        throw std::runtime_error(
            "Input vector length is greater than output vector length");
}

/*
 * Our virtual destructor.
 */
ofdm_divide_vcvc_impl::~ofdm_divide_vcvc_impl() {}

int ofdm_divide_vcvc_impl::calculate_output_stream_length(
    const gr_vector_int& ninput_items)
{
    int noutput_items = ninput_items[0];
    return noutput_items;
}

int ofdm_divide_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* in0 = (const gr_complex*)input_items[0];
    const gr_complex* in1 = (const gr_complex*)input_items[1];
    gr_complex* out = (gr_complex*)output_items[0];

    // Set noutput_items
    noutput_items = ninput_items[0];	// 这行代码设置输出项数等于第一个输入流的项数,确保输出数据的长度与输入保持一致

    // Set output buffer to zero -> is zeropadding
    // 初始化输出缓冲区
    std::memset(out, 0, sizeof(gr_complex) * noutput_items * d_vlen_out);

    // Do division and keep spaces between packets if vlen_out>vlen_in
    // If actual vector is a sync words (given with num_sync_words) do not apply discarded
    // carriers rule

	// 如果 discarded_carriers 列表为空,则禁用舍弃载波的功能,通过将其设置为输入向量长度,确保不会有任何实际载波被舍弃
    int next_discarded_element = 0; // set first discarded element on first vector item
    if (d_discarded_carriers.size() ==
        0) { // set first discarded element on first vector item
        d_discarded_carriers.resize(1);
        d_discarded_carriers[0] = d_vlen_in; // this disables discarded carriers
    }

    // Divide items and discard carriers
    // 载波除法和舍弃逻辑
    for (int k = 0; k < noutput_items; k++) {	// 这个外层循环遍历每一个输出项
        for (int l = 0; l < d_vlen_in; l++) {	// 内层循环遍历每个数据包中的元素或者说是子载波
        	// 这部分检查当前处理的符号是否是同步字。同步字用于帮助接收器定位和解析接收到的信号流。
        	// 如果是同步字(k < d_num_sync_words),则直接进行除法操作,不应用舍弃载波的规则。
        	// 这保证了同步过程的准确性不被舍弃载波影响。
            if (k < d_num_sync_words) { // if actual vector is a sync word
                out[k * d_vlen_out + l] =
                    (in0[k * d_vlen_in + l]) / (in1[k * d_vlen_in + l]);
            } else { // if actual vector is NOT a sync word	// 如果当前处理的不是同步字,则检查当前的子载波是否应被舍弃。
                if (l ==	// 这通过比较当前载波的索引与应被舍弃的载波列表中的当前元素
                    d_discarded_carriers[next_discarded_element]) { // if actual element
                                                                    // shall be discarded
                                                                    // and set to zero
                    out[k * d_vlen_out + l] = 0;	// 如果它们相等,则将输出设为0,表示该载波被舍弃 
                    // 更新 next_discarded_element 指向下一个应被舍弃的载波索引,如果当前已是列表末尾,则重置为0,
                    // 以便下一个包可以重新应用舍弃规则。
                    if (next_discarded_element < d_discarded_carriers.size() - 1)
                        next_discarded_element++; // set next discarded element on next
                                                  // vector item
                    else	// 如果当前载波不在舍弃列表中,则正常执行除法操作,即将输入信号 in0 与 in1 相应元素相除,存储结果到输出数组 out 中
                        next_discarded_element =
                            0; // if item is last one jump back to first item in vector
                } else {       // if actual element shall be divided
                    out[k * d_vlen_out + l] =
                        (in0[k * d_vlen_in + l]) / (in1[k * d_vlen_in + l]);
                }
            }
        }
    }

    // Tell runtime system how many output items we produced.
    return noutput_items;
}

} /* namespace radar */
} /* namespace gr */

二、Matrix Transpose 模块

1、简介

Matrix Transpose 模块主要功能是将输入的矩阵进行转置操作。在信号处理中,矩阵转置可以帮助重新排列数据,以便于进行进一步的处理或分析。例如,在雷达信号处理中,转置操作可能用于在时间和频率域之间转换数据,或者在不同处理阶段调整数据的布局。

2、参数意义

  • Vector length input:这个参数表示输入向量的长度,通常用于定义每个输入数据块的大小
  • Vector length output:指的是转置后的输出向量长度。在矩阵转置中,原始矩阵的行数将成为转置矩阵的列数,因此这个参数应该与输入矩阵的行数相匹配。
  • Packet length key:用于指示每个数据包长度的键(或标签)

3、C++ 具体实现

注释已标注清楚:

代码语言:javascript
复制
/* -*- c++ -*- */
/*
 * Copyright 2014 Communications Engineering Lab, KIT.
 *
 * This is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3, or (at your option)
 * any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this software; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street,
 * Boston, MA 02110-1301, USA.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "transpose_matrix_vcvc_impl.h"
#include <gnuradio/io_signature.h>

namespace gr {
namespace radar {

transpose_matrix_vcvc::sptr
transpose_matrix_vcvc::make(int vlen_in, int vlen_out, std::string len_key)
{
    return gnuradio::get_initial_sptr(
        new transpose_matrix_vcvc_impl(vlen_in, vlen_out, len_key));
}

/*
 * The private constructor
 */
transpose_matrix_vcvc_impl::transpose_matrix_vcvc_impl(int vlen_in,	// 输入向量的长度,这通常代表矩阵中一行的元素数量
                                                       int vlen_out,		   // 输出向量的长度,这将成为矩阵转置后的行长度
                                                       std::string len_key)	   // 指定用于流中的标签
    : gr::tagged_stream_block("transpose_matrix_vcvc",
                              gr::io_signature::make(1, 1, sizeof(gr_complex) * vlen_in),
                              gr::io_signature::make(1, 1, sizeof(gr_complex) * vlen_out),
                              len_key)
{
    d_vlen_in = vlen_in;
    d_vlen_out = vlen_out;

    // Set propagation policy
    set_tag_propagation_policy(TPP_DONT); // does not apply on stream tags!		// 不自动传递任何流标签
}

/*
 * Our virtual destructor.
 */
transpose_matrix_vcvc_impl::~transpose_matrix_vcvc_impl() {}

int transpose_matrix_vcvc_impl::calculate_output_stream_length(
    const gr_vector_int& ninput_items)
{
    int noutput_items = ninput_items[0] * d_vlen_in / d_vlen_out;
    return noutput_items;
}

int transpose_matrix_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];

    // Error handling
    // 检查输入和输出向量长度是否与数据包长度匹配。如果 vlen_in 和 vlen_out 的比例与整数数据包长度不一致,则抛出异常。
    // 这是为了确保数据能够正确地进行矩阵转置。
    if (ninput_items[0] * float(d_vlen_in) / float(d_vlen_out) -
            ninput_items[0] * d_vlen_in / d_vlen_out !=
        0)
        throw std::runtime_error("vlen_in and vlen_out do not match to packet length");

    // Get all tags, reset offset and push to output
    // 获取当前处理块附近的所有流标签,并将它们重新添加到输出流中。这样做是为了保持流标签在数据处理过程中的连续性和正确性
    get_tags_in_range(d_tags, 0, nitems_read(0), nitems_read(0) + 1);
    for (int k = 0; k < d_tags.size(); k++) {
        add_item_tag(
            0, nitems_written(0), d_tags[k].key, d_tags[k].value, d_tags[k].srcid);
    }

    // Set noutput items
    // 这行代码计算并设置输出项的数量,基于输入项数和输入/输出向量长度的比例。
    noutput_items = ninput_items[0] * d_vlen_in / d_vlen_out;

    // Update len key tag
    update_length_tags(noutput_items, 0);

    // Reorganize samples
    // 重组样本
    // 这是矩阵转置的核心部分,双重循环遍历输入数据,按列优先顺序重组样本到输出缓冲区。
    // 外层循环遍历单个输入向量的每个元素,内层循环遍历所有输入向量。
    for (int l = 0; l < d_vlen_in; l++) {           // go through single input vector
        for (int k = 0; k < ninput_items[0]; k++) { // go through all input vectors
            *out++ = in[k * d_vlen_in + l];
        }
    }

    // Tell runtime system how many output items we produced.
    return noutput_items;
}

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

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

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 一、OFDM Divide 模块
    • 1、简介
      • 2、模块作用
        • 3、参数意义
          • 4、C++ 具体实现
          • 二、Matrix Transpose 模块
            • 1、简介
              • 2、参数意义
                • 3、C++ 具体实现
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档