GNU Radio 中 OFDM Serializer 模块是 OFDM Carrier Allocator 逆块,其功能为将 OFDM 子载波的复杂调制符号序列化(并串转换模块),输出复数数据符号作为一个带标签的流,并丢弃导频符号。
// 构造函数, 包含了对输入信号的参数设置、检查和初始化步骤
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); // 设置标签传播政策为不传播,意味着这个块不会自动将输入标签复制到输出标签
}
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;
}
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));
}
// 处理 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;
}