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

文章目录

  • 前言
  • 一、OFDM Divide 模块
    • 1、简介
    • 2、模块作用
    • 3、参数意义
    • 4、C++ 具体实现
  • 二、Matrix Transpose 模块
    • 1、简介
    • 2、参数意义
    • 3、C++ 具体实现


前言

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++ 具体实现

注释已标注清楚:

/* -*- 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++ 具体实现

注释已标注清楚:

/* -*- 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 */

我的qq:2442391036,欢迎交流!


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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

力扣HOT100 - 72. 编辑距离

解题思路&#xff1a; 动态规划 class Solution {public int minDistance(String word1, String word2) {int n1 word1.length();int n2 word2.length();int[][] dp new int[n1 1][n2 1];for (int j 1; j < n2; j) dp[0][j] dp[0][j - 1] 1;for (int i 1; i < …

【Android安全】AOSP版本对应编号| AOSP版本适配Pixel或Nexus型号 | 驱动脚本下载地址

AOSP版本对应编号 https://source.android.com/docs/setup/about/build-numbers?hlzh-cn#source-code-tags-and-builds 例如android-8.1.0_r1 对应的编号是OPM1.171019.011 可以适配Pixel 2 XL AOSP驱动脚本下载 编译AOSP时&#xff0c;需要Google的驱动&#xff0c;后面才…

SaToken+SpringBoot+Redis前后端分离登录认证

目录 前言一、创建工程项目&#x1f38d;1.1 创建后端工程1.2 创建前端工程 二、业务代码&#x1f38a;后端代码前端代码 三、测试参考资料 前言 Sa-Token 是一款 Java 语言的权限认证框架&#xff0c;提供了灵活、高效、易用的权限认证和会话管理功能。它是 SpringBoot、Spri…

解除网页禁止选择

控制台输入以下命令 复制&#xff1a;javascript:void(document.body.οncοpy) 可选&#xff1a;javascript:void(document.body.onselectstart) 拖拉&#xff1a;javascript:void(document.body.οnmοuseup)

短剧系统源码解析与应用

在数字化时代&#xff0c;短剧作为一种新兴的娱乐形式&#xff0c;因其内容紧凑、节奏快速而受到广大年轻群体的喜爱。短剧系统源码的开发和应用&#xff0c;不仅为创作者提供了一个展示才华的平台&#xff0c;也为观众带来了全新的观看体验。本文将对短剧系统源码进行解析&…

MVSnet 代码详解(pytorch)

大致过一下MVSnet 论文中核心的点对应代码应该怎么写。 forward 函数需要 照片&#xff0c;映射矩阵&#xff0c;以及深度值。 照片的shape是 &#xff08;1&#xff0c;5,3&#xff0c;1184,1600&#xff09;代表着1个batch,5张图片&#xff0c;然后一次是每张图片的channel和…

【软考】设计模式之装饰器模式

目录 1. 说明2. 应用场景3. 结构图4. 构成5. 适用性6. 优点7. 缺点8. java示例 1. 说明 1.动态地给一个对象添加一些额外的职责。2.Decorator Pattern。3.就增加功能而言&#xff0c;装饰器模式比生成子类更加灵活。4.一种在不改变现有对象结构的情况下&#xff0c;动态地给对…

quartz定时任务

Quartz 数据结构 quartz采用完全二叉树&#xff1a;除了最后一层每一层节点都是满的&#xff0c;而且最后一层靠左排列。 二叉树节点个数规则&#xff1a;每层从左开始&#xff0c;第一层只有一个&#xff0c;就是2的0次幂&#xff0c;第二层两个就是2的1次幂&#xff0c;第三…

Go 和 Delphi 定义可变参数函数的对比

使用可变参数函数具有灵活性、重用性、简化调用等优点&#xff0c;各个语言有各自定义可变参数函数的方法&#xff0c;也有通用的处理方法&#xff0c;比如使用数组、定义参数结构体、使用泛型等。 这里总结记录一下 go、delphi 的常用的定义可变参数函数的方式&#xff01; 一…

Docker安装MongoDB(Linux版)

文章目录 前言一、Docker环境的准备1.安装依赖2.安装Docker 二、使用Docker安装MongoDB1.mongo版本选取2.拉取合适的镜像3.宿主机创建MongoDB需要挂载的文件夹4.第一次无认证创建mongo用户5.启动需要认证的mongo容器 问题汇总总结 前言 本文章主要介绍在Centos系统&#xff0c…

delphi fmx 跨平台文件浏览器

很多人在找delphi fmx 开发的 android下的文件浏览器 现在她来了 支持android,ios android12 测试通过 代码: object Form1: TForm1Left = 0Top = 0Caption = Form1ClientHeight = 549ClientWidth = 340FormFactor.Width = 320FormFactor.Height = 480FormFactor.Dev…

【GDAL】GDAL库学习(C#版本)

1.GDAL 2.VS2022配置GDAL环境&#xff08;C#&#xff09; VS2022工具–NuGet包管理器–管理解决方案的NuGet程序包&#xff0c;直接安装GDAL包。 并且直接用应用到当前的控制台程序中。 找一张tiff格式的图片&#xff0c;或者用格式转换网站&#xff1a;https://www.zamzar.c…

Web前端开发技术、详细文章、(例子)html 列表、有序列表、无序列表、列表嵌套

目录 列表概述 列表类型与标记符号 无序列表 语法&#xff1a; 语法说明&#xff1a; 无序列表标记的 type 属性及其说明 代码解释 有序列表 基本语法 属性说明 1、列表 o1标记的属性 2、列表项li标记的属性 有序列表 o1标记的属性、值 代码解释 列表嵌套 基本…

【Qt】深入探索Qt主窗口与菜单栏:构建高效用户界面的实践指南

文章目录 前言1. 什么是Main Window?2. 详细了解一下其中的 菜单栏&#xff1a;2.1. 创建菜单栏2.2. 添加快捷键2.3. 添加子菜单2.4. 添加分割线2.5. 添加图标 3. 内存泄漏问题&#xff1a;总结 前言 在现代软件开发中&#xff0c;用户界面的设计对于提升用户体验至关重要。Q…

秀某动预约抢票脚本

秀某动预约抢票脚本 小白操作-仅供学习参考 主要流程和功能 初始化和配置变量: confirm_url 和 login_url: 分别存储登录和确认订单的URL。 wait_time: 用户输入的提前多少秒开始执行。 start_time: 开售时间。 DEBUG: 调试标志&#xff0c;用于控制脚本的行为。 浏览…

并网逆变器学习笔记9---VSG控制

参考文献&#xff1a;《新型电力系统主动构网机理与技术路径》 “构网技术一般包含下垂控制&#xff0c;功率同步控制&#xff0c;虚拟同步机控制&#xff0c;直接功率控制&#xff0c;虚拟振荡器控制 等。其中&#xff0c;虚拟同步机技术&#xff0c;即 VSG&#xff0c;因其物…

css扇形菜单动画效果

菜单组件 IntelligentAnalysis.vue 中间圆形区域可以换个图片 <template><div class"intel-analysis"><div class"info" :class"{ close-animation: !showMenu }"><div class"middle"></div><div cl…

协变(List泛型作为方法参数时的父类子类问题)

有段时间没搞.net的项目了&#xff08;没办法&#xff0c;谁让国内JAVA流行是事实&#xff09;。最近又回归.net&#xff08;哪里需要哪里搬~&#xff09;。 接收到需求后&#xff0c;一顿输出&#xff0c;结果…咦?编译失败??? 错误信息&#xff1a; CS1503:参数1:无法…

阿里云 EMR Serverless Spark 版开启免费公测

阿里云 EMR Serverless Spark 版是一款云原生&#xff0c;专为大规模数据处理和分析而设计的全托管 Serverless 产品。它为企业提供了一站式的数据平台服务&#xff0c;包括任务开发、调试、调度和运维等&#xff0c;极大地简化了数据处理的全生命周期工作流程。使用 EMR Serve…

win11安装MySQL

目录[-] 1. 1. 下载2. 2. 安装 参考文档&#xff1a;MySQL :: MySQL 8.4 Reference Manual 1. 下载 mysql官网下载msi安装程序&#xff1a;MySQL :: Begin Your Download 2. 安装 运行下载的mis程序,逐步安装。 安装模式&#xff1a; complete; 进入配置&#xff1a; data di…