GNU Radio之OFDM Carrier Allocator底层C++实现

文章目录

  • 前言
  • 一、OFDM Carrier Allocator 简介
  • 二、底层 C++ 实现
    • 1、make 函数
    • 2、ofdm_carrier_allocator_cvc_impl 函数
    • 3、calculate_output_stream_length 函数
    • 4、work 函数
    • 5、~ofdm_carrier_allocator_cvc_impl 函数
  • 三、OFDM 数据格式


前言

OFDM Carrier Allocator 是 OFDM 子载波分配模块,也即串并转换模块。该模块的作用是给每个子载波分配相应的值,数据相应地实现串并转换。本文记录其底层 C++ 代码实现。


一、OFDM Carrier Allocator 简介

OFDM Carrier Allocator:OFDM 子载波分配模块,也即串并转换模块。该模块的作用是给每个子载波分配相应的值,数据相应地实现串并转换。

双击 OFDM Carrier Allocator 这个模块,会出现下面这样的属性图:
在这里插入图片描述
官方示例中这里设总载波个数为 64,载波类型包括以下几种:数据子载波(Occupied carriers)、导频子载波(Pilot carriers)、虚拟子载波(Virtual carriers, VC)和直流子载波(DC carrier)。 其中数据子载波可以用来携带数据和同步字,导频载波用于发射特定符号数据,空载波的值默认为 0。在试验中发送端 OFDM 子载波的编号规则是:为了保持左右对称,将 64 个子载波编号为 [-32,31]。其中,最左边 6 个子载波 [-32,-27] 和最右边的 5 个子载波 [26,31] 称为虚拟载波,即不使用的载波它的值默认为 0,其作用是为了减少带外功率和抑制 ACI;编号为[-26,-22]、[-20,-8]、[-6,-1]、[1,6]、[8,20]、[22,26] 都是数据子载波;编号为 -21、-7、7、21 为导频子载波;编号为 0 为直流子载波,作用是降低 OFDM 信号的峰值信噪比以及硬件电路的复杂度;

由此可知,64 个子载波中有效子载波只有 48 个这样我们便可以计算出承载一帧有效数据(包括帧头和负载共 448 个符号)所需要的 OFDM 符号数(将 64 个子载波组成的并行数据称为一个 OFDM 符号),即 448/48=9.33 个 OFDM 符号,也就是说传输一帧有效数据需要 10 个 OFDM 符号。另外,为了接收端解调方便,实验中的 OFDM 信号需要加入 2 个符号的同步字置于一帧有效数据的开头。所以,最后传输的一帧数据中包含了 12 个 OFDM 字符, 2 个同步字和 10 个有效数据

二、底层 C++ 实现

根据维基百科官方资料可以看到其实现的 C++ 代码所在的源文件:OFDM_Carrier_Allocator
在这里插入图片描述
这里我们重点查看 ofdm_carrier_allocator_cvc_impl.cc 文件的内容,使用 source insight 我们可以看到这 5 个实现函数:
在这里插入图片描述
当你在 GNU Radio Companion (GRC) 图中使用 OFDM Carrier Allocator 模块时,模块的生命周期会遵循特定的调用顺序,涉及你提到的函数。这些函数的调用顺序反映了模块从创建到执行再到销毁的整个过程。以下是一个高层次的概览:

    1. make()
    • 作用:这是一个静态工厂方法,用于创建 OFDM Carrier Allocator 模块的实例。在 GRC 生成的 Python 代码中,当流图初始化并创建模块实例时,会首先调用此方法
    • 调用时机:流图初始化时。
    1. ofdm_carrier_allocator_cvc_impl()
    • 作用:这是构造函数,由 make() 方法内部调用,用于初始化模块实例。它设置模块的初始状态,包括配置参数和内部变量的初始化。
    • 调用时机:紧随 make() 方法之后。
    1. calculate_output_stream_length(const gr_vector_int& ninput_items)
    • 作用:这个方法计算输出流的长度,基于输入项的数量。它用于确保流控制逻辑正确处理输入和输出之间的大小关系,尽管它可能不在所有模块中都显式实现。
    • 调用时机:这个方法在处理数据(即调用 work() 方法)之前被调用,但具体是否被调用以及调用时机取决于 GNU Radio 框架的版本和具体实现。
    1. work(int noutput_items, gr_vector_int& ninput_items, gr_vector_const_void_star& input_items, gr_vector_void_star& output_items)
    • 作用:这是核心处理函数,负责实际的数据处理逻辑,包括读取输入数据,执行 OFDM 载波分配,以及输出处理后的数据
    • 调用时机:流图运行时,反复调用。每次处理一批数据时都会调用。
    1. ~ofdm_carrier_allocator_cvc_impl()
    • 作用:析构函数,用于清理资源,如内部分配的内存等。
    • 调用时机:模块销毁时调用,比如流图停止运行并进行清理时。

make() 开始,通过构造函数初始化,到 work() 方法的重复调用处理数据,再到析构函数的清理工作,这一系列函数定义了 OFDM Carrier Allocator 模块在 GNU Radio 流图中的生命周期和行为。

1、make 函数

ofdm_carrier_allocator_cvc::sptr ofdm_carrier_allocator_cvc::make(
    int fft_len,
    const std::vector<std::vector<int>>& occupied_carriers,
    const std::vector<std::vector<int>>& pilot_carriers,
    const std::vector<std::vector<gr_complex>>& pilot_symbols,
    const std::vector<std::vector<gr_complex>>& sync_words,
    const std::string& len_tag_key,
    const bool output_is_shifted)
{
    return gnuradio::get_initial_sptr(
        new ofdm_carrier_allocator_cvc_impl(fft_len,
                                            occupied_carriers,
                                            pilot_carriers,
                                            pilot_symbols,
                                            sync_words,
                                            len_tag_key,
                                            output_is_shifted));
}

这段代码展示了 GNU Radio 中一个典型的工厂方法模式实现,用于创建和初始化 ofdm_carrier_allocator_cvc 模块的一个实例。这个方法属于 ofdm_carrier_allocator_cvc 类,是一个静态成员函数,意味着它可以不通过对象实例而直接调用。这个方法的目的是简化对象的创建和管理,同时隐藏具体实现类 ofdm_carrier_allocator_cvc_impl 的细节。

  • 函数参数
    • fft_len:整数,指定FFT(快速傅立叶变换)的长度。
    • occupied_carriers:一个二维整数向量,指定了哪些载波被用于传输数据。
    • pilot_carriers:一个二维整数向量,指定了哪些载波被用作导频。
    • pilot_symbols:一个二维 gr_complex 向量,包含导频载波上的符号。
    • sync_words:一个二维 gr_complex 向量,包含用于帧同步的同步字。
    • len_tag_key:字符串,指定了长度标签的键,用于流标签系统中。
    • output_is_shifted:布尔值,指示输出是否应该被频移。
  • 函数实现
    函数体内部,make 方法通过调用 new 来创建 ofdm_carrier_allocator_cvc_impl 的一个新实例,将所有接收到的参数传递给其构造函数。然后,它使用 gnuradio::get_initial_sptr 函数将裸指针包装为智能指针(gnuradio::get_initial_sptr 是 GNU Radio中用于创建智能指针的辅助函数,保证了对象的安全管理和自动内存管理)。
  • 智能指针
    返回值是 ofdm_carrier_allocator_cvc::sptr,即指向 ofdm_carrier_allocator_cvc 的智能指针。在 GNU Radio 中,智能指针主要用于管理模块实例的生命周期,避免内存泄露。智能指针在对象不再被使用时会自动释放关联的资源。

2、ofdm_carrier_allocator_cvc_impl 函数

这段代码负责在 OFDM 系统中配置和准备用于传输的帧,包括数据载波、导频载波及其符号,以及同步字的分配。通过精确控制这些参数,可以优化信号的传输效率和质量,同时简化接收端的信号处理流程。

ofdm_carrier_allocator_cvc_impl::ofdm_carrier_allocator_cvc_impl(
	// 参数说明:
    int fft_len,												// FFT(快速傅里叶变换)的长度。
    const std::vector<std::vector<int>>& occupied_carriers,		// 被占用的载波,即用于传输数据的频率位置。
    const std::vector<std::vector<int>>& pilot_carriers,		// 导频载波,用于辅助接收端的信道估计。
    const std::vector<std::vector<gr_complex>>& pilot_symbols,	// 导频符号,已知的信号,用于帮助接收端进行信道估计。
    const std::vector<std::vector<gr_complex>>& sync_words,		// 同步字,用于帧同步。
    const std::string& len_tag_key,								// 长度标签键,用于标记流的长度。
    const bool output_is_shifted)								// 指示输出是否被移位的布尔值。
    
	// 构造函数初始化列表 tagged_stream_block 表明该组件是一个标记流块,用于处理带有特定标记的数据流。
	: tagged_stream_block("ofdm_carrier_allocator_cvc",
                          io_signature::make(1, 1, sizeof(gr_complex)),
                          io_signature::make(1, 1, sizeof(gr_complex) * fft_len),
                          len_tag_key),
    // 成员变量初始化:
      d_fft_len(fft_len),						// FFT长度
      d_occupied_carriers(occupied_carriers),	// 被占用的载波
      d_pilot_carriers(pilot_carriers),			// 导频载波
      d_pilot_symbols(pilot_symbols),			// 导频符号
      d_sync_words(sync_words),					// 同步字
      d_symbols_per_set(0),						// 记录在一个OFDM符号集中,所有被占用的载波上分配的数据和导频符号的总数;"集"指的是一个OFDM帧
      d_output_is_shifted(output_is_shifted)	// 指示输出是否被移位的布尔值。
{
    // Sanity checks
    // If that is is null, the input is wrong -> force user to use ((),) in python
    
    // 检查被占用的载波是否为空,如果是,则抛出异常。
    if (d_occupied_carriers.empty()) {
        throw std::invalid_argument(
            "Occupied carriers must be of type vector of vector i.e. ((),).");
    }

	// 检查每个数据载波索引是否在有效范围内(即非负且小于FFT长度),并对于output_is_shifted为真的情况,调整载波索引
    for (unsigned i = 0; i < d_occupied_carriers.size(); i++) {
        for (unsigned j = 0; j < d_occupied_carriers[i].size(); j++) {
            if (occupied_carriers[i][j] < 0) {
                d_occupied_carriers[i][j] += d_fft_len;
            }
            if (d_occupied_carriers[i][j] > d_fft_len || d_occupied_carriers[i][j] < 0) {
                throw std::invalid_argument("data carrier index out of bounds");
            }
            if (d_output_is_shifted) {
                d_occupied_carriers[i][j] =
                    (d_occupied_carriers[i][j] + fft_len / 2) % fft_len;
            }
        }
    }

	// 检查  导频载波是否为空,如果是,则抛出异常。
    if (d_pilot_carriers.empty()) {
        throw std::invalid_argument(
            "Pilot carriers must be of type vector of vector i.e. ((),).");
    }

	// 检查每个导频载波索引是否在有效范围内(即非负且小于FFT长度),并对于output_is_shifted为真的情况,调整载波索引
    for (unsigned i = 0; i < d_pilot_carriers.size(); i++) {
        for (unsigned j = 0; j < d_pilot_carriers[i].size(); j++) {
            if (d_pilot_carriers[i][j] < 0) {
                d_pilot_carriers[i][j] += d_fft_len;
            }
            if (d_pilot_carriers[i][j] > d_fft_len || d_pilot_carriers[i][j] < 0) {
                throw std::invalid_argument("pilot carrier index out of bounds");
            }
            if (d_output_is_shifted) {
                d_pilot_carriers[i][j] = (d_pilot_carriers[i][j] + fft_len / 2) % fft_len;
            }
        }
    }

	// 检查导频符号是否为空,如果是,则抛出异常。
    if (d_pilot_symbols.empty()) {
        throw std::invalid_argument(
            "Pilot symbols must be of type vector of vector i.e. ((),).");
    }

	// 确保每个导频载波的大小与相应的导频符号向量的大小匹配。
    for (unsigned i = 0; i < std::max(d_pilot_carriers.size(), d_pilot_symbols.size());
         i++) {
        if (d_pilot_carriers[i % d_pilot_carriers.size()].size() !=
            d_pilot_symbols[i % d_pilot_symbols.size()].size()) {
            throw std::invalid_argument("pilot_carriers do not match pilot_symbols");
        }
    }

	// 验证同步字的长度与 FFT 长度相匹配。
    for (unsigned i = 0; i < d_sync_words.size(); i++) {
        if (d_sync_words[i].size() != (unsigned)d_fft_len) {
            throw std::invalid_argument("sync words must be fft length");
        }
    }

	// 计算每组被占用的符号总数。
    for (unsigned i = 0; i < d_occupied_carriers.size(); i++) {
        d_symbols_per_set += d_occupied_carriers[i].size();
    }
    set_tag_propagation_policy(TPP_DONT);	// 置标记传播策略为 TPP_DONT,不传播任何标记。
    set_relative_rate((uint64_t)d_symbols_per_set, (uint64_t)d_occupied_carriers.size());	// 设置相对速率,影响处理数据的速率。
}

3、calculate_output_stream_length 函数

这段代码是 ofdm_carrier_allocator_cvc_impl 类的一个成员函数,用于计算输出流的长度,基于输入项的数量。这个函数对于确保 OFDM 调制过程中输入和输出流的长度匹配至关重要。

int ofdm_carrier_allocator_cvc_impl::calculate_output_stream_length(
    const gr_vector_int& ninput_items) 	// 一个整数向量,其中每个元素代表对应输入流中的项目数目; 对于大多数基于流的处理模块,这个向量可能只有一个元素,因为它们通常只处理一个输入流。
{
    int nin = ninput_items[0];			// 表示输入流中的项目数
    int nout = (nin / d_symbols_per_set) * d_occupied_carriers.size();	// 计算的输出项的基本数目
    int k = 0;

	// 处理余下的输入项; 使用一个循环来处理不足以形成一个完整 OFDM 符号集的余下输入项。
    for (int i = 0; i < nin % d_symbols_per_set; k++) {	// i 变量用于累加当前处理的输入项数目,而 k 变量用于迭代被占用的载波集合。
        nout++;
        i += d_occupied_carriers[k % d_occupied_carriers.size()].size();	// 确保了即使在处理最后一批输入项时,也能正确考虑每个被占用载波集合的大小
    }
    return nout + d_sync_words.size();	// 这个长度包括了基于输入项的计算结果,以及为同步字预留的额外输出项
}

这个函数通过精确计算在给定输入项数目的情况下,需要生成多少个输出项来确保 OFDM 调制的正确性。它不仅考虑了每个 OFDM 符号集内的符号总数和被占用载波的集合大小,还考虑了同步字的需求。通过这种方式,ofdm_carrier_allocator_cvc_impl 能够确保输出流长度恰当地匹配输入流长度和 OFDM 调制过程的需求。

示例中 64 个子载波编号为 [-32,31],将其变为 [0, 63]
在这里插入图片描述

4、work 函数

work 函数负责在 OFDM 调制过程中的载波分配工作,包括同步字、数据符号和导频符号的映射。通过这种方式,确保了 OFDM 信号能够被正确地组装并准备好发送。这个过程不仅涉及到信号处理的实际操作,还包括流标签的管理,以确保流中的元数据被正确处理和传递。

int ofdm_carrier_allocator_cvc_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];
    std::vector<tag_t> tags;

    memset((void*)out, 0x00, sizeof(gr_complex) * d_fft_len * noutput_items);	// 初始化输出缓冲区
    // Copy Sync word
    for (unsigned i = 0; i < d_sync_words.size(); i++) {
        memcpy((void*)out, (void*)&d_sync_words[i][0], sizeof(gr_complex) * d_fft_len);	// 将预定义的同步字复制到输出流的开始部分,这些同步字用于接收端的帧同步。
        out += d_fft_len;
    }

    // Copy data symbols
   	// 将输入数据符号映射到被占用的 OFDM 载波上。这一过程考虑了载波的集合配置和当前处理的数据符号集合。
    long n_ofdm_symbols = 0; // Number of output items
    int curr_set = 0;
    int symbols_to_allocate = d_occupied_carriers[0].size();
    int symbols_allocated = 0;
    for (int i = 0; i < ninput_items[0]; i++) {
        if (symbols_allocated == 0) {
            // Copy all tags associated with these input symbols onto this OFDM symbol
            // 复制与输入符号相关联的所有标签到输出 OFDM 符号上,保持流标签系统中的信息。
            get_tags_in_range(
                tags,
                0,
                nitems_read(0) + i,
                nitems_read(0) + std::min(i + symbols_to_allocate, (int)ninput_items[0]));
            for (unsigned t = 0; t < tags.size(); t++) {
                add_item_tag(0,
                             nitems_written(0) + n_ofdm_symbols +
                                 (n_ofdm_symbols == 0 ? 0 : d_sync_words.size()),
                             tags[t].key,
                             tags[t].value);
            }
            n_ofdm_symbols++;
        }
        out[(n_ofdm_symbols - 1) * d_fft_len +
            d_occupied_carriers[curr_set][symbols_allocated]] = in[i];
        symbols_allocated++;
        if (symbols_allocated == symbols_to_allocate) {
            curr_set = (curr_set + 1) % d_occupied_carriers.size();
            symbols_to_allocate = d_occupied_carriers[curr_set].size();
            symbols_allocated = 0;
        }
    }
    // Copy pilot symbols
    // 在数据符号映射完成后,将导频符号映射到指定的导频载波上。这些导频符号对于接收端的信道估计至关重要。
    for (int i = 0; i < n_ofdm_symbols; i++) {
        for (unsigned k = 0; k < d_pilot_carriers[i % d_pilot_carriers.size()].size();
             k++) {
            out[i * d_fft_len + d_pilot_carriers[i % d_pilot_carriers.size()][k]] =
                d_pilot_symbols[i % d_pilot_symbols.size()][k];
        }
    }

    return n_ofdm_symbols + d_sync_words.size();	// 返回处理完成的OFDM符号数量,包括数据符号和同步字。
}

5、~ofdm_carrier_allocator_cvc_impl 函数

析构函数,用于清理资源,如内部分配的内存等。

ofdm_carrier_allocator_cvc_impl::~ofdm_carrier_allocator_cvc_impl() {}

三、OFDM 数据格式

经过子载波分配模块和 IFFT 后的 OFDM 数据格式大致如下面形式:
在这里插入图片描述


我的qq:2442391036,欢迎交流!


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

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

相关文章

Spring Boot项目启动过程中为什么日志打印没有显示完整包名呢?

一、前言 不知道大家注意过没有&#xff0c;在Spring Boot项目启动过程中日志打印并没有显示完整的报名&#xff0c;而是显示一些o.a.c&#xff0c;o.s.web形式的包名&#xff0c;如下图&#xff1a; 这是为什么呢&#xff1f; 二、原理 首先&#xff0c;我们先看一下Spring…

ArrayList和LinkedList有什么区别?

ArrayList和LinkedList的区别 ArrayList 和 LinkedList 是 Java 中常用的两种集合类&#xff0c;它们都实现了 List 接口&#xff0c;但在内部实现和性能上有一些区别。 内部实现&#xff1a; ArrayList 使用数组实现&#xff0c;它的元素在内存中是连续存储的&#xff0c;每…

软件部署资源计算工具:精确评估资源需求

软件部署资源计算工具&#xff1a;精确评估资源需求 在当今快速发展的信息技术时代&#xff0c;软件部署已成为企业运营不可或缺的一部分。然而&#xff0c;一个常见的挑战是如何精确评估软件部署所需的资源。资源评估不仅关系到软件的性能和稳定性&#xff0c;还直接影响到成…

Spring Boot:Web开发之三大组件的整合

Spring Boot 前言Spring Boot 整合 ServletSpring Boot 整合 FilterSpring Boot 整合 Listener 前言 在 Web 开发中&#xff0c;Servlet 、Filter 和 Listener 是 Java Web 应用中的三大组件。Servlet 是 Java 代码&#xff0c;通过 Java 的 API 动态的向客户端输出内容。Filt…

SpringMvc之映射器HandlerMapping

简介 在springmvc的处理流程中&#xff0c;第一步就是查询请求对应的映射器&#xff0c;然后组装成处理器链处理请求&#xff0c;本文意在梳理该过程 重要实现 HandlerMapping是一个接口&#xff0c;该接口用于通过HttpServletRequest寻找对应的处理器&#xff0c;接口介绍如下…

python学习15:python中的input语句

python中的input语句 我们前面学习过print语句&#xff0c;可以将内容输出到屏幕上&#xff1b;在python中&#xff0c;与之对应的还有一个input语句&#xff0c;用来获取键盘输入。 数据输出&#xff1a;print 数据输入&#xff1a;input 使用上也很简单&#xff1a; 使用inp…

31-3 文件包含漏洞 - 文件包含漏洞利用(CVE-2021-3019:Lanproxy 任意文件读取漏洞复现)

一、Lanproxy简介: lanproxy是一个将局域网个人电脑、服务器代理到公网的内网穿透工具,支持TCP流量转发,可支持任何TCP上层协议(访问内网网站、本地支付接口调试、SSH访问、远程桌面等)。 二、漏洞概述: Lanproxy1.0 版本存在目录遍历漏洞,可通过绕过路径限制(../)来…

量化交易入门(二十四)MTM指标买卖逻辑和回测

MTM指标可以用来指导买卖决策,其基本逻辑如下: 买入信号: 当MTM从负值上穿0线,并向上突破某个阈值(如20)时,表明上升动力充足,可以考虑买入。当MTM在0线上方并持续上升,创出新高时,表明上升趋势强劲,可以考虑加仓或持有。 卖出信号: 当MTM从正值下穿0线,并向下突破某个阈值(如-…

C++中stack的用法及其解析

一、stack的介绍 1.stack是一个容器适配器&#xff0c;它的名字叫做栈 专门用在后进先出的上下文环境中的&#xff0c;它的删除与插入操作只能从容器的一端进行。形象一点&#xff0c;就好像一个容器里放东西&#xff0c;先放进去的就在底部&#xff0c;要想拿出来&#xff0c;…

华为昇腾asend

昇腾Ascend C编程语言 Ascend C原生支持C/C编程规范&#xff0c;通过多层接口抽象、并行编程范式、孪生调试等技术&#xff0c;极大提高了算子的开发效率&#xff0c;帮助AI 参考文章 手把手教你在昇腾平台上搭建PyTorch训练环境 - 哔哩哔哩 (bilibili.com)https://www.bilibi…

PCL点云处理之重复随机采样一致性(RRANSAC法)平面拟合(二百三十七)

PCL点云处理之重复随机采样一致性(RRANSAC法)平面拟合(二百三十七) 一、算法介绍二、算法实现1.代码2.结果一、算法介绍 pcl::SAC_RRANSAC"是 PCL库中的一个方法,是 RANSAC 方法的改进版本,通过多次重复采样和模型拟合来提高鲁棒性。RRANSAC 的思想是在 RANSAC 的基…

双网卡环境概率出现DNS解析错误

测试环境 VMware Rocky Linux 9 虚拟机, 双网卡(eth0和eth1)配置如下&#xff1a; eth0 10.206.216.27/24 DNS 10.204.16.18 eth1 192.168.1.27/24 DNS 192.168.1.1问题描述 手动配置eth1的DNS后&#xff0c;网络不通&#xff0c;通过抓包发现是eth1的DNS server配置有误…

最新海外投资理财源码 amazon多语言投资理财系统源码 区块链理财项目平台源码 共享充电宝系统

一款新UI的海外多语言刷单系统&#xff0c;支持后台在线添加订单派单、预约派单、余额宝等功能 源码下载&#xff1a;https://download.csdn.net/download/m0_66047725/88949885 更多资源下载&#xff1a;关注我。

Vue使用el-statistic和el-card显示大屏中的统计数据

​ 一、页面内容&#xff1a; <el-row :gutter"20"><el-col :span"6"><el-card class"box-card"><div><el-statisticgroup-separator",":precision"2":value"value2":title"tit…

上位机图像处理和嵌入式模块部署(qmacvisual图像修复)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 qmacvisual提供了一个图像修复的功能。所谓的图像修复&#xff0c;就是对图像中缺省的部分进行修补&#xff0c;它的操作&#xff0c;其实分成两个…

深入理解SSL协议:从理论到实践(二)

前言 这是一篇关于SSL协议的技术文章&#xff0c;有理论知识&#xff0c;但又兼具一定的实战性&#xff0c;文章的主要内容分享了SSL协议的核心概念、工作原理、常见的应用场景&#xff0c;以及就https这种实际应用场景&#xff0c;又着重分享具体的工作原理以及如何实现https…

【性能优化】 【回溯】 【字符串】1307. 口算难题

作者推荐 视频算法专题 本文涉及知识点 数学 回溯 字符串 性能优化 LeetCode1307. 口算难题 给你一个方程&#xff0c;左边用 words 表示&#xff0c;右边用 result 表示。 你需要根据以下规则检查方程是否可解&#xff1a; 每个字符都会被解码成一位数字&#xff08;0 - …

R: 网状Meta分析进行模型构建及图形绘制

网状meta分析的制作步骤主要包括&#xff1a; 1. 绘制网状证据图 2. 普通Meta分析&#xff08;两两之间的直接比较&#xff09; 3. 网状Meta分析&#xff08;整合直接比较和间接比较的结果&#xff0c;绘制相关图形&#xff09; 4. 绘制累积概率排序图 5. 三个假设的检验…

【Linux】网络基础1

欢迎来到Cefler的博客&#x1f601; &#x1f54c;博客主页&#xff1a;折纸花满衣 &#x1f3e0;个人专栏&#xff1a;题目解析 目录 &#x1f449;&#x1f3fb;一些常见网络设备&#x1f449;&#x1f3fb;网络协议(栈)&#x1f449;&#x1f3fb;协议分层OSI参考模型每个层…

CCF-CSP真题202206-2《寻宝!大冒险!》

题目背景 暑假要到了。可惜由于种种原因&#xff0c;小 P 原本的出游计划取消。失望的小 P 只能留在西西艾弗岛上度过一个略显单调的假期……直到…… 某天&#xff0c;小 P 获得了一张神秘的藏宝图。 问题描述 西西艾弗岛上种有 n 棵树&#xff0c;这些树的具体位置记录在…