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

news2025/4/19 6:53:51

文章目录

  • 前言
  • 一、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,欢迎交流!


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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

防火墙技术基础篇:配置基本转发策略(安全策略)

防火墙基础基础篇&#xff1a;配置基本转发策略&#xff08;安全策略&#xff09; 1 什么是安全策略&#xff1f; 安全策略指的是用于保护网络的规则。它是由管理员在系统中配置&#xff0c;决定了哪些流量可以通过&#xff0c;哪些流量应该被阻断。安全策略是防火墙产品的一…

智能科技的新风潮:探索Web3与物联网结合

引言 随着科技的不断进步和创新&#xff0c;智能科技正成为新时代的主旋律。在这个充满活力和变革的时代&#xff0c;Web3技术与物联网的结合成为了一股新的风潮。本文将深入探讨这一新趋势&#xff0c;揭示Web3与物联网结合的意义、挑战和前景。 Web3技术的特点与优势 区块链…

Git系列:git init 深入理解及其使用技巧

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…

好的架构是进化来的,不是设计来的

很多年前&#xff0c;读了子柳老师的《淘宝技术这十年》。这本书成为了我的架构启蒙书&#xff0c;书中的一句话像种子一样深埋在我的脑海里&#xff1a;“好的架构是进化来的&#xff0c;不是设计来的”。 2015 年&#xff0c;我加入神州专车订单研发团队&#xff0c;亲历了专…

抄表:现代生活中的数据采集关键

1.界定与发源 抄表&#xff0c;简单的说&#xff0c;指从各种各样计量机器设备(如智能水表、电度表、天然气表等)载入做好记录使用量的全过程。这一概念自工业化时代至今就出现了&#xff0c;最初由人工进行&#xff0c;伴随着科技创新&#xff0c;如今已经演化出自动化和远程…

保研面试408复习 5——操作系统(死锁)、计网(TCP和UDP)

文章目录 1、操作系统一、死锁的定义、原因和必要条件a.死锁的定义b.死锁的原因c.死锁产生的必要条件 二、如何预防死锁&#xff1f; 2、计算机网络一、TCP和UDP的相同点二、TCP和UDP的区别 标记文字记忆&#xff0c;加粗文字注意&#xff0c;普通文字理解。 1、操作系统 一、…

深度学习之基于Matlab的BP神经网络交通标志识别

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 一、项目背景与意义 随着智能交通系统&#xff08;ITS&#xff09;的快速发展&#xff0c;交通标志识别&#xff0…

【DevOps】深入浅出:Jenkins 性能监控全解析

目录 一、监控指标&#xff1a;把握系统健康状况 1、资源利用率&#xff1a; 2、 任务执行效率&#xff1a; 3、系统稳定性&#xff1a; 二、监控工具&#xff1a;选择合适的利器 1、Jenkins 内置监控 1.1、Jenkins Performance Plugin&#xff1a;系统性能指标的直观展…

出谈论点云文件pcd加载01

刚写完基于potree开发地图水印效果的时候&#xff0c;在网上分享实例&#xff0c;刚发出去&#xff0c;竟然被人喷了&#xff0c;这么简单的实例&#xff0c;竟然好意思发群里&#xff0c;哎… 好无奈&#xff01; 不过我还是坚持我的想法&#xff0c;大家看到文章后&#xff0…

赛事|基于SprinBoot+vue的CSGO赛事管理系统(源码+数据库+文档)

CSGO赛事管理系统 目录 基于SprinBootvue的CSGO赛事管理系统 一、前言 二、系统设计 三、系统功能设计 1系统功能模块 2管理员功能模块 3参赛战队功能模块 4合作方功能模块 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&…

单片机通信协议(1):SPI简介

关于SPI SPI&#xff08;串行外设接口&#xff09;是板载设备间通信接口之一。它是由摩托罗拉公司&#xff08;飞思卡尔半导体&#xff09;推出的。由于其简单性和通用性&#xff0c;它被纳入各种外围设备中&#xff0c;并与飞利浦I2C总线并列。 SPI的三线或四线信号数量比IIC…

英伟达:AI之火还在燃烧!

昨晚&#xff0c;全球市场屏息以待的一家公司财报终于发布了&#xff0c;没有超出大家预期的是&#xff0c;他还是超预期了。 大家当然都知道我们要说的是——英伟达&#xff01; 如今&#xff0c;全球大模型之Z激Z正酣&#xff0c;AI芯片装备竞赛需求猛烈&#xff0c;作为AI…

几个速度比较快的 Linux 开源镜像站

搜狐开源镜像站 https://mirrors.sohu.com/ File Name CPAN/ FreeBSD/ QpenBSD/ RockyL apache/ archlinux/ centos/ ceph/ cygwin/ debian/ debian–cd/ debian-security/ deepin/ deepin-cd/ docker-ce/ fedora/ fedora-epel/ gentoo/ lib/ mysql/ nginx/ opensuse/ php/ ubu…

手撕C语言题典——轮转数组

目录 前言 一&#xff0c;思路 1&#xff09;暴力求解 O(N^2) 2&#xff09;三段逆置 二&#xff0c;代码实现 前言 随着C语言的深入&#xff0c;我们准备转向C方面学习&#xff0c;学习C之前我们需要搞懂时间复杂度&#xff0c;这个蛮重要的&#xff0c;我们将通过几个题…

实时沟通,即时转化:WeChat与HubSpot对接,打造高效营销闭环

在全球化和数字化的浪潮下&#xff0c;企业面临着前所未有的挑战和机遇。WeChat作为中国乃至亚洲市场的重要社交媒体平台&#xff0c;拥有庞大的用户群体和丰富的营销资源。而HubSpot&#xff0c;作为全球领先的客户关系管理&#xff08;CRM&#xff09;软件&#xff0c;为企业…

基于日志或 gv$sql_audit 分析 OB 异常重试 SQL

本文以 SQL 异常重试场景为例&#xff0c;使用基于 日志文件 和 gv$sql_aduit 视图 这两种方式&#xff0c;找出具体的报错原因。 作者&#xff1a;郑增权&#xff0c;爱可生 DBA 团队成员&#xff0c;OceanBase 和 MySQL 数据库技术爱好者。 爱可生开源社区出品&#xff0c;原…

1941springboot VUE 服务机构评估管理系统开发mysql数据库web结构java编程计算机网页源码maven项目

一、源码特点 springboot VUE服务机构评估管理系统是一套完善的完整信息管理类型系统&#xff0c;结合springboot框架和VUE完成本系统&#xff0c;对理解JSP java编程开发语言有帮助系统采用springboot框架&#xff08;MVC模式开发&#xff09;&#xff0c;系统具有完整的源代…

深入解读HTTP状态码:分类、含义、应用场景与故障排查指南

HTTP状态码作为超文本传输协议(HTTP)响应的重要组成部分,为客户端与服务器之间的交互提供了清晰的状态反馈。本文将全面展开对HTTP状态码的深入解读,涵盖其分类、具体含义、典型应用场景以及在故障排查中的实用价值,旨在帮助开发者与运维人员更好地理解和应对各类HTTP响应…

冯喜运:5.24黄金今日能否回调?日内国际黄金美原油操作策略

【黄金消息面分析】&#xff1a;在过去的半个世纪里&#xff0c;美国国债作为买入持有的投资手段&#xff0c;轻松超越了黄金。然而&#xff0c;如今债券作为终极避险资产的地位正面临着前所未有的挑战。传统上&#xff0c;投资者将美国国债视为一种超安全的投资&#xff0c;因…

[emailprotected](7)父子通信,传递元素内容

目录 1&#xff0c;children 属性2&#xff0c;多个属性 普通对象等&#xff0c;可以通过变量直接传递&#xff0c;那类似 vue 中的 slot 插槽&#xff0c;如何传递元素内容&#xff1f; 1&#xff0c;children 属性 实际上&#xff0c;写在自定义组件标签的内部代码&#xf…