GNU Radio之OFDM Channel Estimation底层C++实现

文章目录

  • 前言
  • 一、 OFDM Channel Estimation 模块简介
  • 二、C++ 具体实现
    • 1、初始化和配置参数
    • 2、forecast 函数
    • 3、计算载波偏移量
    • 4、提取信道响应
    • 5、核心的数据处理任务


前言

OFDM Channel Estimation 模块的功能是根据前导码(同步字)估计 OFDM 的信道和粗略频率偏移,本文对 OFDM Channel Estimation 模块的底层 C++ 源码进行剖析。


一、 OFDM Channel Estimation 模块简介

在这里插入图片描述
OFDM Channel Estimation模块的主要目的是从接收的OFDM符号中恢复出发送时的信道条件。主要包括以下功能:

  • 信道估计:
    • 这个模块核心的功能是估计 OFDM 系统中的信道特性。这包括计算信道的频率响应,以便可以对接收到的信号进行适当的校正,以恢复原始发送的数据。信道估计通常利用已知的同步或导频符号来测量信道对这些已知符号的影响。
  • 载波频率偏移估计:
    • 在 OFDM 系统中,载波频率偏移是接收机和发射机之间存在的频率误差。ofdm_chanest_vcvc_impl 类通过分析接收到的 OFDM 符号来估计这一偏移,这对确保数据正确解调是至关重要的。
  • 生成信道抽头(Channel Taps):
    • 信道抽头是描述信道频率响应的复数值,这些复数值可以直接应用于信号解调和均衡过程中。在 OFDM 系统中,每个子载波的信道响应可以被视为一个抽头。
  • 处理和传递元数据:
    • 这个类还负责在GNU Radio的流图中处理和传递相关的元数据,如信道估计结果和载波偏移信息。这些信息通常通过标签(tags)的形式添加到数据流中,供后续的处理块使用。

注意:这个模块只是做估计,未进行均衡,均衡由 OFDM Frame Equalizer 模块实现

二、C++ 具体实现

ofdm_chanest_vcvc_impl 实现了以下关键方法:

  • forecast()
    • 该方法为调度器提供了关于块如何根据输入生成输出的信息。具体来说,它告诉调度器在执行处理之前需要多少输入数据。
  • general_work()
    • 这是块的主要处理函数,它处理输入数据,执行信道估计和载波偏移估计,并生成输出数据。此函数还负责将计算出的信道信息和其他相关元数据标签插入到输出流中。
  • get_carr_offset()get_chan_taps()
    • 这些辅助函数用于计算载波偏移和提取信道抽头,是信道估计过程的核心部分。

1、初始化和配置参数

构造函数 ofdm_chanest_vcvc_impl,实现初始化和配置信道估计的各种参数

// 构造函数,初始化和配置信道估计的各种参数
ofdm_chanest_vcvc_impl::ofdm_chanest_vcvc_impl(	
    const std::vector<gr_complex>& sync_symbol1,	// 同步符号, 用于信道估计
    const std::vector<gr_complex>& sync_symbol2,	// 同步符号, 用于信道估计
    int n_data_symbols,								// 数据符号的数量,表示每次处理的数据符号数
    int eq_noise_red_len,							// 均衡噪声减少的长度,用于设置信道估计中的一些内部处理
    int max_carr_offset,							// 最大载波偏移,用于粗略频率估计
    bool force_one_sync_symbol)						// 用于控制是否强制只使用一个同步符号进行信道估计
    : block("ofdm_chanest_vcvc",
            io_signature::make(1, 1, sizeof(gr_complex) * sync_symbol1.size()),		// 示这个模块有一个输入端口,每个输入项是一个复数向量,向量的长度等于 sync_symbol1.size()。
            io_signature::make(1, 2, sizeof(gr_complex) * sync_symbol1.size())),	// 表示这个模块有一个或两个输出端口,输出数据格式与输入相同。
      d_fft_len(sync_symbol1.size()),	// FFT的长度
      d_n_data_syms(n_data_symbols),
      d_n_sync_syms(1),
      d_eq_noise_red_len(eq_noise_red_len),
      d_ref_sym((!sync_symbol2.empty() && !force_one_sync_symbol) ? sync_symbol2	// 参考同步符号为 sync_symbol2
                                                                  : sync_symbol1),
      d_corr_v(sync_symbol2),	// 用于信道估计的向量,用于存储相关性向量
	  // 用于存储已知和新的符号差异。
	  d_known_symbol_diffs(0, 0),	
      d_new_symbol_diffs(0, 0),	
      d_first_active_carrier(0),	// 第一个活跃子载波的索引  
      d_last_active_carrier(sync_symbol2.size() - 1),	// 最后一个活跃子载波的索引
      d_interpolate(false)			// 不需要插值
{
    // Set index of first and last active carrier
	// ******************************寻找活跃载波**********************
	/*
		这两个循环用于确定第二个同步字中第一个和最后一个非零(即活跃)载波的索引。
		这是为了确定数据中的有效范围。
	*/
    for (int i = 0; i < d_fft_len; i++) {
        if (d_ref_sym[i] != gr_complex(0, 0)) {
            d_first_active_carrier = i;
            break;
        }
    }
    for (int i = d_fft_len - 1; i >= 0; i--) {
        if (d_ref_sym[i] != gr_complex(0, 0)) {
            d_last_active_carrier = i;
            break;
        }
    }

    // Sanity checks
    // ******************************合理性检查**********************
	/*
		这部分代码首先检查两个同步符号的长度是否相等,如果不等则抛出异常。
		接着,根据是否强制使用一个同步符号来调整同步符号的数量。
		如果只有一个同步符号且下一个载波是零,则开启插值模式。
	*/
	if (!sync_symbol2.empty()) {
        if (sync_symbol1.size() != sync_symbol2.size()) {
            throw std::invalid_argument("sync symbols must have equal length.");
        }
        if (!force_one_sync_symbol) {
            d_n_sync_syms = 2;
        }
    } else {
        if (sync_symbol1[d_first_active_carrier + 1] == gr_complex(0, 0)) {
            d_last_active_carrier++;
            d_interpolate = true;
        }
    }

    // Set up coarse freq estimation info
    // Allow all possible values:
    // ******************************设置频率估计参数**********************
	/*
		这部分设置最大负载和正载波偏移量,并确保这些偏移量为偶数,这是因为同步算法要求。
	*/
	d_max_neg_carr_offset = -d_first_active_carrier;	// 系统可以容忍的最大向下(或向负方向)的频率偏移量,表示为载波数量。负载波偏移意味着接收频率低于发射频率。
    d_max_pos_carr_offset = d_fft_len - d_last_active_carrier - 1;	// 系统可以容忍的最大向上(或向正方向)的频率偏移量,同样表示为载波数量。正载波偏移意味着接收频率高于发射频率。
    if (max_carr_offset != -1) {
        d_max_neg_carr_offset = std::max(-max_carr_offset, d_max_neg_carr_offset);
        d_max_pos_carr_offset = std::min(max_carr_offset, d_max_pos_carr_offset);
    }
    // Carrier offsets must be even
    if (d_max_neg_carr_offset % 2)
        d_max_neg_carr_offset++;
    if (d_max_pos_carr_offset % 2)
        d_max_pos_carr_offset--;

	// ******************************处理相关性向量**********************
	/*
		如果使用两个同步符号,计算每个载波的相关性。如果只使用一个,
		重新设置相关向量并计算已知符号之间的差异。
	*/
	if (d_n_sync_syms == 2) {
        for (int i = 0; i < d_fft_len; i++) {
            if (sync_symbol1[i] == gr_complex(0, 0)) {
                d_corr_v[i] = gr_complex(0, 0);
            } else {
                d_corr_v[i] /= sync_symbol1[i];		// 同步字2 ÷ 同步字1
            }
        }
    } else {
        d_corr_v.resize(0, 0);
        d_known_symbol_diffs.resize(d_fft_len, 0);
        d_new_symbol_diffs.resize(d_fft_len, 0);
        for (int i = d_first_active_carrier;
             i < d_last_active_carrier - 2 && i < d_fft_len - 2;
             i += 2) {
            d_known_symbol_diffs[i] = std::norm(sync_symbol1[i] - sync_symbol1[i + 2]);
        }
    }

	// ******************************设置输出和速率**********************
    set_output_multiple(d_n_data_syms);		// 设置输出的数量
    set_relative_rate((uint64_t)d_n_data_syms, (uint64_t)(d_n_data_syms + d_n_sync_syms)); // 设置输出的相对速率
    set_tag_propagation_policy(TPP_DONT);	// 设置输出的标签传播策略
}

2、forecast 函数

forecast 函数是由框架在调度块(block)执行之前调用的。这个函数的主要作用是告诉调度器(scheduler),在实际调用处理函数(如 general_work 或 work 函数)之前,块(block)需要多少输入项(samples)来产生预期的输出项。这一机制确保在执行处理函数时,块有足够的数据来进行处理,从而避免处理函数中出现缓冲区下溢的情况。

// forecast 方法在 GNU Radio 中的用途是为调度器提供关于数据依赖关系的信息,
// 即它告诉系统在产生一定数量的输出之前,需要多少输入。这个方法对于确保块在
// 有足够的输入数据处理之前不被调用是非常重要的。
void ofdm_chanest_vcvc_impl::forecast(int noutput_items,		// 预期的输出项数。在这个上下文中,它指的是调度器计划产生的输出数据块的数量
                                      gr_vector_int& ninput_items_required)	// 用于存储每个输入流所需的输入项数
{	
	// ************************逻辑解释************************
	// 这个 forecast 方法实现的基本思想是:为了产生 noutput_items 个输出,每个输出都需要 d_n_data_syms 个数据符号,但每组输入还包括一定数量的同步符号 (d_n_sync_syms)。
	// 因此,我们需要从输入流中获取足够的数据来覆盖这两部分的需求。
	// 这种计算方式确保了无论何时调度器决定调用这个块处理数据时,块都能有足够的输入数据来满足其输出产量的需求,从而避免在数据不足时处理数据,这是确保数据流正确性的关键一环。


	// 计算并设置第一个输入流(索引为0)所需的输入项数
	// (noutput_items / d_n_data_syms): 将预期的输出项数除以每组数据符号的数量,这个操作基本上在计算为了生成所需的输出数量,需要处理多少组数据。
	// (d_n_data_syms + d_n_sync_syms): 计算得到的每组数据的数量乘以每组中数据符号和同步符号的总和
	ninput_items_required[0] =
        (noutput_items / d_n_data_syms) * (d_n_data_syms + d_n_sync_syms);	
}

3、计算载波偏移量

// 用于计算并返回载波偏移量
int ofdm_chanest_vcvc_impl::get_carr_offset(const gr_complex* sync_sym1,	// 同步符号,用于计算载波偏移
                                            const gr_complex* sync_sym2)
{
    int carr_offset = 0;
    if (!d_corr_v.empty()) {
        // Use Schmidl & Cox method
        // 相关性的估计方法,如Schmidl & Cox方法
        float Bg_max = 0;	// 初始化最大相关性度量为0
        // g here is 2g in the paper
        // 从最大负载波偏移量到最大正载波偏移量遍历,步长为2
        for (int g = d_max_neg_carr_offset; g <= d_max_pos_carr_offset; g += 2) {
			// 初始化一个临时复数用于计算当前偏移量 g 下的相关性
			gr_complex tmp = gr_complex(0, 0);
			// 对每个FFT长度内的点,如果相关向量在该点不为零,则计算该点在两个同步符号上的相关性,并累加到 tmp。
            for (int k = 0; k < d_fft_len; k++) {
                if (d_corr_v[k] != gr_complex(0, 0)) {
                    tmp += std::conj(sync_sym1[k + g]) * std::conj(d_corr_v[k]) *
                           sync_sym2[k + g];
                }
            }
			// 如果当前的 tmp 的绝对值大于已知的最大值,则更新最大值和对应的载波偏移量。
            if (std::abs(tmp) > Bg_max) {
                Bg_max = std::abs(tmp);
                carr_offset = g;
            }
        }
    } else {
        // Correlate
        std::fill(d_new_symbol_diffs.begin(), d_new_symbol_diffs.end(), 0);
        for (int i = 0; i < d_fft_len - 2; i++) {
            d_new_symbol_diffs[i] = std::norm(sync_sym1[i] - sync_sym1[i + 2]);
        }

        float sum;
        float max = 0;
        for (int g = d_max_neg_carr_offset; g <= d_max_pos_carr_offset; g += 2) {
            sum = 0;
            for (int j = 0; j < d_fft_len; j++) {
                if (d_known_symbol_diffs[j]) {
                    sum += (d_known_symbol_diffs[j] * d_new_symbol_diffs[j + g]);
                }
                if (sum > max) {
                    max = sum;
                    carr_offset = g;
                }
            }
        }
    }
    return carr_offset;
}

他这里的理论参考的是 Robust Frequency and Timing Synchronization for OFDM. Timothy M. Schmidl and Donald C. Cox, Fellow, IEEE 的论文内容。

4、提取信道响应

// 用于从同步符号中提取信道响应,即“信道抽头”(channel taps)。这些信道抽头代表了在多径环境下,信道对每个频率的响应。
void ofdm_chanest_vcvc_impl::get_chan_taps(const gr_complex* sync_sym1,
                                           const gr_complex* sync_sym2,
                                           int carr_offset,					// 载波偏移
                                           std::vector<gr_complex>& taps)	// 用于存储计算出的信道抽头
{
	// ***************选择使用的同步符号****************
    const gr_complex* sym = ((d_n_sync_syms == 2) ? sync_sym2 : sync_sym1);	// 使用 sync_sym2 同步符号数组
	// ***************初始化信道抽头向量****************
	std::fill(taps.begin(), taps.end(), gr_complex(0, 0));
	// ***************设置循环边界****************
	/*
		初始化循环的起始和结束索引。根据载波偏移调整这些索引,
		以避免数组越界。载波偏移向正方向时,从偏移处开始;
		向负方向时,结束点前移。
	*/
    int loop_start = 0;
    int loop_end = d_fft_len;
    if (carr_offset > 0) {
        loop_start = carr_offset;
    } else if (carr_offset < 0) {
        loop_end = d_fft_len + carr_offset;
    }

	// ***************计算信道抽头****************
	/*
		遍历有效的FFT点范围。只有当参考符号在相应的位置不为零时,才计算信道抽头,避免除零错误。
		信道抽头是通过将当前同步符号(经过信道后)除以参考同步符号得到的。
	*/
    for (int i = loop_start; i < loop_end; i++) {
        if ((d_ref_sym[i - carr_offset] != gr_complex(0, 0))) {
            taps[i - carr_offset] = sym[i] / d_ref_sym[i - carr_offset];
        }
    }

	// ***************插值处理****************
	/*
		如果启用了插值 (d_interpolate),对信道抽头进行插值处理,填补那些没有直接计算的点。
		这通常用于提高信道估计的平滑性和准确性。
	*/
    if (d_interpolate) {
        for (int i = d_first_active_carrier + 1; i < d_last_active_carrier; i += 2) {
            taps[i] = taps[i - 1];
        }
        taps[d_last_active_carrier] = taps[d_last_active_carrier - 1];
    }

	// ***************噪声降低处理(未实现)****************
    if (d_eq_noise_red_len) {
        // TODO
        // 1) IFFT
        // 2) Set all elements > d_eq_noise_red_len to zero
        // 3) FFT
    }
}

5、核心的数据处理任务

int ofdm_chanest_vcvc_impl::general_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];
    const int framesize = d_n_sync_syms + d_n_data_syms;	// 定义处理的总帧大小,包括同步符号和数据符号的数量。

    // Channel info estimation
    // 信道信息估计
    int carr_offset = get_carr_offset(in, in + d_fft_len);		// 计算载波偏移量
    std::vector<gr_complex> chan_taps(d_fft_len, 0);			// 存储信道抽头
    get_chan_taps(in, in + d_fft_len, carr_offset, chan_taps);	// 填充chan_taps向量
	// 在输出流的特定位置添加标签,标识载波偏移和信道抽头的信息。
	add_item_tag(0,
                 nitems_written(0),
                 pmt::string_to_symbol("ofdm_sync_carr_offset"),
                 pmt::from_long(carr_offset));
    add_item_tag(0,
                 nitems_written(0),
                 pmt::string_to_symbol("ofdm_sync_chan_taps"),
                 pmt::init_c32vector(d_fft_len, chan_taps));

    // Copy data symbols
    // 复制数据符号到输出
    if (output_items.size() == 2) {	// 如果输出项数为2,则将chan_taps数据复制到第二个输出。
        gr_complex* out_chantaps = ((gr_complex*)output_items[1]);
        memcpy((void*)out_chantaps, (void*)&chan_taps[0], sizeof(gr_complex) * d_fft_len);
        produce(1, 1);
    }
	// 将输入中的数据符号部分复制到输出
    memcpy((void*)out,
           (void*)&in[d_n_sync_syms * d_fft_len],
           sizeof(gr_complex) * d_fft_len * d_n_data_syms);

    // Propagate tags
    // 传递标签
    /*
		从输入流获取所有标签并调整它们的位置,考虑到同步符号的存在。
	*/
    std::vector<gr::tag_t> tags;
    get_tags_in_range(tags, 0, nitems_read(0), nitems_read(0) + framesize);
    for (unsigned t = 0; t < tags.size(); t++) {
        int offset = tags[t].offset - nitems_read(0);
        if (offset < d_n_sync_syms) {
            offset = 0;
        } else {
            offset -= d_n_sync_syms;
        }
        tags[t].offset = offset + nitems_written(0);
        add_item_tag(0, tags[t]);
    }

	// 生成和消耗数据
	// 指示产生的输出项数和消耗的输入项数。
    produce(0, d_n_data_syms);
    consume_each(framesize);
    return WORK_CALLED_PRODUCE;
}

我的qq:2442391036,欢迎交流!


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

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

相关文章

FileLink内外网文件摆渡系统产品介绍

在现代企业中&#xff0c;往往存在着多个网络、系统之间的数据孤岛问题&#xff0c;数据难以互相访问和共享。 一、常用的内外网文件摆渡方式 传统的数据交换方式往往需要人工介入&#xff0c;效率低下且容易出错。如&#xff1a;U盘、FTP、VPN等&#xff0c;极易引发各种各样…

CSS常见的 9 个单位汇总!

你好&#xff0c;我是云桃桃。 一个希望帮助更多朋友快速入门 WEB 前端的程序媛。 云桃桃-大专生&#xff0c;一枚程序媛&#xff0c;感谢关注。回复 “前端基础题”&#xff0c;可免费获得前端基础 100 题汇总&#xff0c;回复 “前端工具”&#xff0c;可获取 Web 开发工具合…

银行卡归属地查询API接口快速对接

银行卡归属地查询API接口指的是通过银行卡号查询该银行卡详细信息&#xff0c;包括银行卡名称、卡种、卡品牌、发卡行、编号以及归属地等信息&#xff0c;支持一千多家银行返回归属地信息&#xff0c;那么银行卡归属地查询API接口如何快速对接呢&#xff1f; 首先找到有做银行…

短视频橱窗好物带货者必看:如何解决无商品素材无收益还限流的烦恼?

随着短视频橱窗带货越来越火爆&#xff0c;许多人发现通过短视频橱窗好物带货素材APP不仅可以提升创作效果&#xff0c;还能轻松赚取佣金。下面&#xff0c;为您推荐三款一键领取并直接发布的抖音短视频素材APP&#xff0c;确保您在创作短视频时&#xff0c;既有高质量的素材&a…

Facebook’s Tectonic Filesystem: Efficiency from Exascale——论文阅读

FAST 2021 Paper 分布式元数据论文阅读笔记整理 背景 Blob storage 用来存放大量的文本、图片、视频等非结构化数据 包含 EB 级别的数据 存储内容大小不一&#xff0c;大小几KB到几MB不等 要求低时延 使用 Haystack 和 F4 Data warehouse 存放用于数据分析和机器学习的…

AI与新能源催生新增长,电子制造业如何提升预测力与连接力?

国产替代和新基建带来的结构性机遇&#xff0c;AI和新能源汽车行业的增长所带来的需求提升&#xff0c;都给电子制造行业以乐观的理由。但是&#xff0c;不少企业的客户经营管理、供需平衡与供应链协同等所面临的挑战仍在&#xff0c;如何为行业高质量增长持续注入动能&#xf…

使用mmdetection来训练自己的数据集(visdrone)(四)结果分析

测试 python tools/test.py <your-config-file> <your-model-weights-file> --out <save-pickle-path>关于test.py 的命令行 parser.add_argument(--out,typestr,helpdump predictions to a pickle file for offline evaluation)计算量、参数量计算脚本 pyth…

PNPM - nodejs 包管理

文章目录 一、关于 PNPM开发动机1、节省磁盘空间2、提升安装速度3、创建一个 non-flat node_modules 文件夹 二、安装通过 npm 安装 pnpm通过 Homebrew 安装 pnpm 三、pnpm CLI1、与 npm 的差异2、参数-C <path>, --dir <path>-w, --workspace-root 3、命令4、环境…

如何监控ANR

在 Android 开发中&#xff0c;“应用程序无响应”&#xff08;ANR&#xff09;是一种常见的问题&#xff0c;当应用程序在主线程上执行过长时间的操作时就会出现。本文将详细介绍ANR的成因&#xff0c;以及如何有效监控和预防这一问题。 什么是ANR&#xff1f; ANR&#xff…

PADS看图常用操作

1.放大缩小&#xff1a; 方法1&#xff0c;CTRL滚轮 方法2&#xff0c;按住滚轮&#xff0c;前后移动鼠标。 方法3&#xff0c;PageUP&#xff0c;PageDown按键 2.PADS layout只显示单层&#xff08;当前层&#xff09;怎么操作&#xff0c;而不显示其他层 第一步&#xff1a;…

Android双向认证配置过程

1&#xff08;可以绕过&#xff09;准备过程 为了让这个教程可以一直复用&#xff0c;打算直接写一个双向认证的APP作为素材。 工具&#xff1a; ●protecle&#xff08;签名文件转换&#xff09; ●keytool&#xff08;java自己就有&#xff09; ●openssl&#xff08;apache里…

使用工具速记

文章目录 一、sqlyoy登录账号信息迁移二、idea导入之前的已配置的idea信息三、设置windows UI大小四、其他 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一、sqlyoy登录账号信息迁移 工具(sqlyog上面菜单栏)->导入导出详情->选择要导出的账号…

安卓悬浮窗权限检查

目录 悬浮窗权限代码检测悬浮窗功能 悬浮窗权限 请求了这个权限后&#xff0c;app的权限管理中会有「显示悬浮窗」的权限选项。后面会引导用户去开启这个权限。 <uses-permission android:name"android.permission.SYSTEM_ALERT_WINDOW" />代码检测悬浮窗功能…

算法基础课 贪心算法模板题笔记

AcWing算法基础课 贪心算法模板题笔记 贪心得到的答案 > 最优解 贪心得到的答案 < 最优解 局部最优 -> 全局最优 文章目录 1 区间问题例1&#xff1a;区间选点例2&#xff1a;最大不相交区间数量例3&#xff1a;区间分组例4&#xff1a;区间覆盖 2 Huffman树例&…

2023最新!Git2.40.0于win10环境下的安装

2023最新&#xff01;Git2.40.0于win10环境下的安装 git官网地址&#xff1a;https://git-scm.com/download/win/ 导航 文章目录 2023最新&#xff01;Git2.40.0于win10环境下的安装导航一、下载Git二、安装Git三、检验 一、下载Git Git官网选择自己所需的版本下载 二、安装…

恒驰喜报 | 12强精英对决,恒驰勇夺华为云数字化转型演讲赛亚军

4月28日落幕的“2024年上海销售伙伴赛马专项行动——华为云数字化转型演讲比赛”上&#xff0c;恒驰信息华为云解决方案首席架构师&#xff0c;在上海12家华为云核心伙伴的同台竞技中&#xff0c;凭借对数字化转型及华为云服务的深刻理解&#xff0c;夺得演讲比赛“第二名”的好…

单纯形投影算法

目录 一&#xff0c;任意点到平移坐标轴面的投影 1&#xff0c;求解目标 2&#xff0c;转换变量 3&#xff0c;求解结果 4&#xff0c;f(t)的导数 5&#xff0c;f(t)的最小值 二&#xff0c;任意点到标准单纯形的投影 1&#xff0c;求解目标 2&#xff0c;公式变形 3…

MATLAB数值类型

MATLAB 数值 MATLAB支持各种数字类&#xff0c;包括有符号和无符号整数以及单精度和双精度浮点数。默认情况下&#xff0c;MATLAB将所有数值存储为双精度浮点数。 您可以选择将任何数字或数字数组存储为整数或单精度数字。 所有数值类型都支持基本数组运算和数学运算。 转换…

好看的PPT目录怎么做?推荐2款一键生成目录的AI PPT软件!

一份完整的 PPT 演示文稿&#xff0c;由多个页面组成&#xff0c;包括 PPT 封面页、PPT 目录页、PPT 分隔页&#xff08;PPT 过渡页&#xff09;、PPT 内容页、PPT 结束页&#xff0c;每一个页面类型&#xff0c;在整个 PPT 中起到不同的作用&#xff1a; PPT 封面页是 PPT 的…

蒸镀的氧化硅薄膜为什么有时候是绿色有时候是棕色的?

知识星球&#xff08;星球名&#xff1a;芯片制造与封测社区&#xff0c;星球号&#xff1a;63559049&#xff09;里的学员问&#xff1a;我们用热阻式蒸镀设备镀氧化硅薄膜&#xff0c;出来的颜色有时候会发生变化是什么原因呀&#xff1f;有时候薄膜是绿色有时候是棕色。 氧…