gr-radar 中的 OFDM Divide
模块是GNU Radio中的一个组件,专门用于处理正交频分复用(OFDM)信号。这个模块主要执行复数信号的除法操作,通常用于雷达和通信系统中的信号处理。
下面对这个模块进行介绍并详细分析其底层 C++ 代码实现。
这个模块执行复杂的复数除法,用 in0/in1
进行计算。如果 vlen_out 大于 vlen_in,则额外的空间将填充为零。这可以用于零填充。
下面是引自一篇硕士论文(车联网背景下的雷达通信一体化感知方法研究与平台实现)中讲述矩阵相除的作用:
Vector length input
:这个参数定义了输入向量的长度。在 OFDM 系统中,这通常对应于快速傅里叶变换(FFT)的长度,代表了每个 OFDM 符号中的子载波数量。Vector length output
:这个用来设置输出向量的长度。 Discarded carriers
:这个参数是一个集合,列出了需要在除法操作中被忽略的载波。通过设置这些载波为零,可以在后续处理中排除它们的影响。Number of sync words
:这个参数指定了有多少个同步字在处理时不应用舍弃载波的规则。同步字是用于帮助接收器定位和同步信号的特定数据序列。允许这些部分不受舍弃载波设置的影响,确保信号的同步和稳定性不会受到干扰。Packet length key
:这个参数用于指定在处理数据包时使用的关键字,它标识了数据包的长度。注释已标注清楚:
/* -*- 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 模块主要功能是将输入的矩阵进行转置操作。在信号处理中,矩阵转置可以帮助重新排列数据,以便于进行进一步的处理或分析。例如,在雷达信号处理中,转置操作可能用于在时间和频率域之间转换数据,或者在不同处理阶段调整数据的布局。
Vector length input
:这个参数表示输入向量的长度,通常用于定义每个输入数据块的大小Vector length output
:指的是转置后的输出向量长度。在矩阵转置中,原始矩阵的行数将成为转置矩阵的列数,因此这个参数应该与输入矩阵的行数相匹配。Packet length key
:用于指示每个数据包长度的键(或标签)注释已标注清楚:
/* -*- 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 */