SDR 开发者社区
连接技术与应用,探索软件定义无线电的无限可能。
在这里交流心得、解决难题。
GNU Radio buffer介绍
1、 GNU Radio buffer分配1.1 buffer使用简述GNU Radio数据buffer的管理机制是零拷贝循环缓存,这样能够保证来自源的数据流高效在各个模块之间传输。具体的buffer分配方式如下图所示:图1 buffer的使用方式页管理存储方式,一开始就分配好,buffer的长度是每页的大小的倍数,同时,每个循环缓存映射成两部分,实际使用的只是其中的一半。Buffer的size由用户决定(最大、最小缓存大小设定)。1.2 buffer创建方式针对不同的操作系统,GNU Radio提供不同配置方式。由vmcircbuf classes类型控制实现buffer使用。● MS Windows系统,采用Create File Mapping方式● OSX系统,采用Memory-mapped Temp File方式● Linux/POSIX系统,提供了System V Shared Memory和Memory-mapped Shared Memory两种方式 具体每种方式的建立步骤这里不再赘述,后面根据代码再简单介绍。2、 GNURadio buffer的创建流程GNU Rradio Flowgraph 运行时,每个模块的输出端口都绑定一个循环缓冲区(buffer),通过一个写指针 w_ptr 输出数据,它的下游模块通过一个读指针r_ptr 读取数据。对于每个block的buffer如何进行分配,在block运行的设置连接函数setup_connections()里就对它进行了具体的描述,主要是对buffer的size以及地址进行设定,具体流程如下图所示:图2 buffer分配流程3、 代码分析每个Block通过detail()可以得倒block_detail实例对象指针,而且block的输出端口都绑定一个循环缓冲区(buffer),其分配起始于下面的代码: 上面这部分代码是buffer分配的具体实现,主要是对输出端口对应的缓冲区size大小及输出数据类型做判断,确保buffer分配的有效,然后通过make_buffer实现buffer的分配以及设定循环缓冲区的首地址。Buffer的分配最终是调用VM circular buffer类实现缓存分配并返回基址。前面提到过,针对不同的操作系统提供了不同的内存分配接口,系统默认的buffer分配配置文件路径是$HOME/.gnuradio/prefs/vmcircbuf_default_factory。通过读取文件获取到(如默认 gr::vmcircbuf_sysv_shm_factory),如果没有默认配置或者配置的不正确,则直接获取工厂列表中的第一个。同样,根据获取到的factory,然后通过对应的make进行buffer分配。具体的代码实现参考以下的.h和对应的cc文件(vmcircbuf_createfilemapping.h、vmcircbuf_sysv_shm.h、vmcircbuf_mmap_shm_open.h、vmcircbuf_mmap_tmpfile.h)1、MS Windows系统的实现Windows系统采用createfilemapping 方式实现内存的分配及映射,具体代码的实现逻辑如下:① size大小是page size的整数倍,通过CreateFileMapping获取文件页的句柄;② 通过VirtualAlloc分配2*size大小的内存并返回指针first_tmp;③ 通过MapViewOfFileEx分别将文件页映射到分配的虚拟内存的前后两部分,指针分别对应为first_tmp和first_tmp+size;④ 返回内存地址指针(保证分配的内存大小是2*size)。具体的实现代码见vmcircbuf_createfilemapping::vmcircbuf_createfilemapping::vmcircbuf_createfilemapping(int size): gr::vmcircbuf(size)函数,2、OSX系统的实现苹果系统,查看vmcircbuf_mmap_tmpfile::vmcircbuf_mmap_tmpfile (int size): gr::vmcircbuf (size)函数具体描述,这里不再详细介绍。3、LINUX系统的Mermory-mapped Shared Memory实现查看vmcircbuf_mmap_shm_open::vmcircbuf_mmap_shm_open(int size): gr::vmcircbuf(size)函数具体描述,通过shm_open分配内存,这里不再详细介绍。4、GNU radio默认的buffer分配采用LINUX系统的另外一种实现方式,即System V Shared Memory共享内存方式,主要由shmget、shmat、shmat、shmctl四个函数组成实现。代码的逻辑基本上如下描述:① 获取page size,传入的size大小是page size的整数倍;② 通过shmget分别创建shmid_guard(page size大小)、shmid1(size大小)、shmid2(2*size大小)共享内存对象;③ 通过shmat将各共享内存区对象映射到调用进程的地址空间,将shmid1分别映射到shmid2前半部分和后半部分,并且保证shmid2的两部分内存映射到同样的虚拟空间,同时映射shmid_guard到首地址,且为只读模式;④ 返回内存地址指针(保证分配的内存大小是2*size)。具体的实现代码见文件vmcircbuf_sysv_shm.cc文件的vmcircbuf_sysv_shm::vmcircbuf_sysv_shm(int size): gr::vmcircbuf(size)函数。到此为止,buffer的分配完成,后面通过block建立block之间的连接,对应每个buffer绑定reader,在block的运行中,通过维护r_ptr和w_ptr即可实现buffer的循环使用。4、 LOG跟踪 在flat_flowgraph.cc文件中,将#define FLAT_FLOWGRAPH_DEBUG 0修改为#define FLAT_FLOWGRAPH_DEBUG 1,即打开日志的使能开关,就可以看到各block buffer分配的简单信息,如果需要详细了解分配的各步骤信息,可以在代码中增加打印信息。
GNU Radio Scheduler
1、 Scheduler 任务简述GNU Radio中每个block的调度工作如下图所示,主要包含三个方面:图1 block的调度工作(1) 维护block对应的buffer的读写指针;blcok对应的buffer读写指针在buffer章节介绍中有描述,这里不再赘述。(2) 处理各个block的约束要求Block的约束需求主要是四部分设置:Forecast函数的功能是告诉调度器对于每次输出的item需要多少个输入的items;Output Multiple则是通过函数set_output_multiple(int multiple)设置每次输出items的大小,如果没有足够的数据则不输出。Alignment表明数据对齐,通过函数set_alignment(int multiple)进行设置,保证输入的数据为multiple倍数大小;History主要用来设置buffer读指针的history items,一方面保证我们有足够有效的数据history items超过输出的output_items大小,另一方面它经常用于有因果关系的信号间进行工作。具体使用方式如下图:图2 history使用(3) 完成信息的传输GNU Radio采用消息传递(Message)和流标签(Stream tag)两种机制在block 之间传输信息。其中Stream tag是同步标签,只能单向传输,Message是异步消息,可以向任何方向传输。在GNU Radio流图中,Stream tag实线表示,Message虚线表示。【注意】Stream tag与数据流是并行传输模式,不是插入到原始数据流,也不会改变原始数据流,而是绑定到数据流的某一个样点,只能在block之间传递消息,不能通过天线发送出去。2、 流图调度2.1 模块调度初始化流图建立后,运行流图,创建调度器。对流图的模块进行拓扑排列,获取各个block的vector信息,设置流图中所有模块的done标志,然后循环为各个block创建线程并启动线程body进行信息处理。图3 流图的block初始化流程2.2 模块线程整体处理 各block线程主体处理的流程如下图所示。先前的GNU Radio框架介绍提及到,模块的默认调度机制是TPB模式,即多线程调度。每个block的线程处理主体函数的实现均包含在tpb_thread_body.cc文件中,进入线程body,首先获取block的detail()信息以及当前线程ID,接着绑定线程、设置线程优先级,然后while循环进行消息、数据流的run_one_iteration操作以及返回状态处理。具体介绍代码分析部分再描述。图4 各block线程的主体处理流程2.3 模块线程核心处理run_one_iteration()函数是线程主体运行的核心,包含了模块的主要功能实现。图4 block线程核心处理流程总的来说,run_one_iteration()函数按照模块类型(信源、信宿以及中间模块三种)进行逐一处理。针对block的每个输出端口,检查各自对应的buffer的剩余空间,从而获得block可以输出的items数目。如果没有用于输出的足够的空间,则返回BLKD_OUT(输出阻塞);同时,根据前面计算出的输出items数目,计算出所需要的输入items数目(通过forecast函数),综合检查输入的items数目是否满足要求,不满足则返回BLKD_IN;如果输入的items足够,也有足够的空间存放输出的items,则执行block::general_work()完成模块的功能。2.3.1 try again流程 该流程主要从以下几个方面进行逻辑的判断和处理: 1、如果是固定速率的block,有一些专门的限制条件,单独分析;2、未设置Output Multiple,针对Alignment是否设置进行判断;3、调用forecast计算输入items,判断数据是否满足要求,回调work函数进行处理;4、其他的异常情况判断,返回相应状态处理。具体的实现流程如下图所示:图5 try again处理流程2.3.2 work流程 work流程通过获取buffer指针,调用各个block的general_work()函数进行数据处理,如果启用了work处理函数的性能计数器,则统计性能,接着根据返回值进行相应处理并返回结果状态值。图6 work处理流程2.3.3 DONE流程 该流程代表当前数据信息处理完成,然后设置block的done标志,通知上下游模块可以进行数据信息的输入和输出。图7 done处理流程3、 代码走读3.1 调度器初始化流图运行的时候,会根据系统选择的调度器模式(TPB)进行调度器的创建和初始化(对流图的模块进行拓扑排列,获取各个block的vector信息(模块的上下游关系),设置流图中所有模块的done标志,循环为各个block创建线程)。 3.2 线程处理主体函数线程主体函数处理部分,首先是获取线程ID,绑定线程,然后设置各个block的线程优先级,清除block的finish标志。接下就是信息的处理部分了,先遍历消息队列,如果有当前消息注册的句柄,则从队列中POP,然后调用该block的handler分发消息;否则将该消息塞进队列,判断队列里的最大消息数,超过容量则将最开始进队列的消息删除掉。如果不是消息而是流模块,则判断输入输出数据信息调用run_one_iteration()处理,返回处理的状态结果值。然后就针对各返回状态值进行处理:◆ READY:通知上下游模块有足够的buffer空间和数据输出;◆ READY_NO_OUTPUT:通知上游模块有buffer空间可以使用;◆ DONE:告知上下游模块信息处理完成;◆ BLKD_IN:等待数据的输入或者新的消息处理;◆ BLKD_OUT:等待数据的缓存空间释放或者新的消息处理。3.3 流模块数据处理 代码在block_executor.cc文件中,主要根据block类型(信源、信宿以及中间模块)进行分别处理。3.3.1信源模块该模块只有下游模块,综合计算输出buffer空间,如果足够则调用work处理,否则等待buffer的释放。3.3.2信宿模块该模块只有上游模块,如果输入数据完成且数据不满足block的output multiple,直接返回DONE;如果没有输出则表明没有输入数据,返回BLKD_IN,否则直接进入try again流程处理。3.3.3中间模块中间模块从上游模块接收数据,然后输出到下游模块,在处理时先确认输入的数据,然后计算输出buffer的空间,如果noutput_items值为0,则阻塞中,等待buffer,值为-1则表明已经完成,否则进入try again进行进一步的判断和分析。3.3.4 try again处理 该部分针对block的限制需求进行相关判断分析和处理,主要包含fixed_rate、output_multiple_set、alignment、forecast以及input的check,并返回对应的处理值。如果一切都正常,则获取buffer的r_ptr指针读取输入数据,调用work进行数据的处理。3.3.5 work处理 获取输出buffer的写指针,将输入数据调用general_work处理输出写入buffer。3.3.6其他处理 包括调整剩余待处理的未对齐数据的unaligned属性;如果是标签流,按照新的速率进行数据传递;更新buffer的指针位置;设置新的传输速率等等。4、 LOG日志 在block的调度时,消息模块主要是由框架进行队列的管理及分发,这部分日志没什么好查看,如果想看消息具体的实现细节,则需在各个block的实现文件中增加相应日志信息;流模块则是由调度器循环不断的处理,主要的细节处理在block_executor.cc文件中,将#define ENABLE_LOGGING 0修改为#define ENABLE_LOGGING 1,即打开日志的使能开关,就可以看到各个block在运行中的各步骤打印信息,同样如果要看模块的具体功能实现细节,则需在blcok的work实现中增加相应的日志记录。
GNU Radio代码框架简介
1、GNU Radio框架图1 gnuradio框架 2、GNU Radio系统层次结构图图2 gnuradio系统层次结构3、GNU Radio软件架构 图3 gnuradio软件架构4、代码阅读4.1 block信号处理基类 图4 gnuradio信号处理类(C++)4.2 block应用关系 图5 gnuradio block应用上图中,箭头表示引用关系,如通过调用block::detail()可以得倒block_detail实例对象的指针。block主要关注数据的处理、运算和生成;block_detail主要关注block的输入、输出及连接关系等;buffer主要关注buffer的分配、数据的写入及交换等;buffer_reader主要关注buffer的读取。每个block的输出可以连接到多个block的输入,两个block之间的连接(或者说是数据交换)是通过buffer类的对象完成的。从buffer的角度来看,每个buffer都与一个block的输出和多个block的输入相关联,可以看成是单写入多读取的环形缓冲区。对于一个指向buffer对象的指针buf来说,通过buf->link()可以获得与之关联的上游block指针(该block向buffer中写入);同时,该buffer还暴露出若干个buffer_reader对象指针,用于下游block的读取,每个buffer_reader各自通过link()成员函数与单个下游block相关联。通过调用全局函数buffer_add_reader可以为buffer绑定一个新的reader。4.3 block调度运行机制一个系统由多个block连接而成,并形成一个top_block,通过调用top_block的run()成员函数,gnuradio启动一系列的线程,并通过调度机制将来自于源的数据顺序经过各个block处理,最终送到sink。 图6 gnuradio调度流程 top_block持有一个指向类top_block_impl的数据成员d_impl,进入top_block_impl类的start函数查看: 上述代码执行若干检查后,最后调用阴影部分的代码创建gnuradio的调度器,接着我们继续查看make_scheduler()函数: 代码可以看出,首先检查是否设置了linux的环境变量GR_SCHEDULER,如果没有则使用默认的TPB(thread per block)调度器,否则根据环境变量使用所需要的调度器。TPB(default):multi-threaded schedulerSTS:single-threaded scheduler默认情况下,调用scheduler_tpb::make来创建调度器。 该代码中,对flowgraph中的block进行排序,系统按照排列的结果,逐个block进行调度。最重要的是最后阴影部分的代码,thread_body_wrapper是一个仿函数类,该类用来封装线程的主体函数,其构造函数第一个参数为实际要执行的函数体。该部分代码中,这个参数是tpb_container类的对象。 tpb_container类的定义如下: 对于每个block来说,线程的入口就在tpb_thread_body body(d_block, d_max_noutput_items);每个线程的工作步骤有以下几步:1、每个block对应的线程都有一个while(1)循环2、循环内处理各种消息并运行关键处理函数run_one_iteration()3、run_one_iteration()检查是否有足够多的输入数据和足够的输出空间4、如果是,调用general_work()函数完成block的功能5、否则相应的返回BLKD_IN,BLKD_OUT 可以看出,run_one_iteration()是线程运行的主体,包含了模块的主要功能。其主要流程如下: 总的来说,该函数针对block的每个输出端口,检查各自对应的buffer的剩余空间,从而获得block可以输出的items数目。如果没有用于输出的足够的空间,则返回BLKD_OUT(输出阻塞);同时,根据前面计算出的输出items数目,计算出所需要的输入items数目(通过forecast函数),综合检查输入的items数目是否满足要求,不满足则返回BLKD_IN;如果输入的items足够,也有足够的空间存放输出的items,则执行block::general_work()完成模块的功能。5、代码调试5.1 日志 在block_executor.cc文件中,将#define ENABLE_LOGGING 0修改为#define ENABLE_LOGGING 1,即打开日志的使能开关,就可以看到各个block在运行中的各步骤打印信息。5.2 block的GDB调试 block的调试主要包含以下几个步骤:(1)编译模块代码 cmake的时候,按照以下命令执行cmake .. -DCMAKE_BUILD_TYPE=Debug(2)在生成的py文件最前面加上 import os 最后面的 if __name__ == '__main__':中,main前增加以下两行: print "pid=%d" %(os.getpid(),) raw_input() (3)如果gdb中出现设置断点提示make breakpoint pending on future shared library? 则按照下面步骤操作:打开文件:sudo文件:sudo gedit /etc/sysctl.d/10-prtace.conf修改kernel.yama.ptrace_scope=0然后执行service procps restart(4)在终端中执行python *.py 回车后显示pid,然后在新的终端中“gdb -p pid”进入gdb设置断点后,在显示PID号的终端上再次回车启动流图运行,进入断点调试。6、缩略语简写全称SWIG(Simplified Wrapper and Interface Generator)简化封装接口生成器SDR(Software Defined Radio)软件无线电USRP(Universal software Radio Peripheral)通用软件无线电外围设备GPP(General-Purpose Processors)通用处理器TPB(thread per block)每模块线程STS(single-threaded scheduler)单线程调度
国产软件无线电 RF210 和 USRP B210 核心区别对比
一、产品定位与研发背景区别 1. USRP B210(国外通用 SDR 设备)USRP B210 是国外厂商推出的入门级双通道软件无线电硬件,多年来广泛用于高校通信教学、简易射频信号采集,属于海外商用通用射频设备。短板明显:核心芯片、底层驱动、配套软件均为海外技术,无法适配国产信创环境;设备采购、维保、授权成本高,供货周期不稳定,军工、涉密科研场景存在数据安全风险。2. RF210 国产软件无线电(大尧科技自研国产化 SDR)RF210 是国内自主研发的双通道通用软件无线电设备,核心 FPGA、射频链路、底层 UHD 兼容驱动全部自主开发,专为国内高校新工科实验室、国产化电磁仿真、涉密电子对抗测试打造。核心定位:国产替代 USRP B210,兼顾教学实训与军工科研场景,完美适配飞腾、鲲鹏国产 CPU,麒麟、统信国产操作系统,满足国内信创国产化政策要求。二、硬件射频参数核心差异频率覆盖范围 USRP B210 标准频段 70MHz–6GHz,低频段缺失,无法开展低频长波、短波通信相关实验; RF210 覆盖 1MHz–6GHz,向下拓展至 1MHz 低频区间,支持短波、中波、超宽带信号收发,适配更多雷达、导航、低频通信实训项目。瞬时带宽与采样性能 两者最大瞬时带宽均支持 56MHz,基础采样指标持平; RF210 优化 FPGA 内部信号处理链路,本地实时波形处理延迟更低,多信号同步仿真稳定性优于 B210,适合复杂电磁环境叠加测试。FPGA 主控芯片 USRP B210 采用海外原厂 FPGA,芯片供货受海外管制; RF210 搭载国产 Kintex-7 系列 FPGA,芯片全国产供应链,无断供风险,支持用户二次开发自定义射频算法、跳频模块。通道与时钟同步 二者均为 2 发 2 收双通道; RF210 标配高精度内置 PPS 时钟同步接口,多设备级联搭建多通道电磁仿真系统时同步误差更小,适合组网式雷达信号模拟。三、驱动与软件生态兼容性区别1. 驱动适配USRP B210 仅原生支持 Windows、Ubuntu 海外系统,无官方国产系统适配包,麒麟、统信安装驱动难度大,易出现兼容报错;RF210全兼容 UHD 标准驱动,可直接复用原有基于 USRP 开发的代码工程,同时原生适配 Windows、Ubuntu、银河麒麟、统信 UOS 全系统,信创电脑可即插即用,无需二次移植代码。2. 配套仿真软件USRP B210 依赖 MATLAB、LabVIEW 等国外商用仿真软件,软件授权费用高昂,涉密项目禁止使用;RF210 配套自研天问云教学仿真平台、国产波形开发软件,无需付费海外软件,内置 100 + 通信、雷达、导航教学实验案例,支持本地离线仿真 + 云端远程实训,完全满足高校产教融合需求。四、国产化适配与安全合规差异供应链安全 USRP B210 硬件、核心元器件海外进口,国际局势影响供货周期,部分单位采购受限; RF210 整机元器件、电路板、驱动程序全部国内生产自研,供应链稳定,可大批量供货用于高校实验室批量采购、军工项目配套。涉密 / 信创场景使用权限 USRP B210 属于境外设备,涉密科研、国防电子对抗项目存在数据外泄风险,多数单位采购准入受限; RF210 纯国产软硬件架构,无境外代码后门,符合国产化仪器采购标准,可用于涉密电磁信号模拟、军用通信仿真测试。五、使用成本、售后与技术支持区别采购与长期使用成本 USRP B210 进口设备关税、物流成本高,单台采购价格更高;配套 MATLAB 等软件每年需支付高额授权费;设备故障维修需寄回海外,维修周期长达数月。 RF210 国产自研设备,定价适配国内院校预算,无额外软件授权费用;厂家本地技术团队提供终身免费驱动升级,硬件故障国内原厂快速维修,售后响应时效 1-3 个工作日。教学配套技术服务 USRP 厂商仅提供基础英文文档,无国内高校定制化教学方案; 采购 RF210 可免费获取配套实验讲义、课程教案、线上实训云平台使用权,厂家工程师支持进校搭建通信实验室、开展教师技术培训。六、适用场景选择建议选择 USRP B210 的场景仅普通非涉密高校简易教学、无国产化要求、仅做基础射频演示,无低频、多设备级联仿真需求。优先选用国产 RF210 软件无线电的场景高校新工科、通信电子实验室国产化改造项目;需要适配麒麟、统信国产操作系统、飞腾国产主机;短波、低频通信、多通道雷达导航信号仿真;涉密科研、国防电子对抗、复杂电磁环境模拟;院校远程云端教学、批量搭建分布式实训平台;想要替代 USRP,降低设备与软件长期采购成本。七、总结USRP B210 是成熟的海外入门 SDR 设备,基础射频指标可满足简单实验,但存在供货受限、无法适配信创系统、软件成本高、涉密场景禁用等短板;国产 RF210 软件无线电在保留 UHD 兼容、对标 B210 核心射频性能的基础上,实现全国产化软硬件替代,拓展低频带宽、适配国产软硬件生态,配套免费教学仿真平台,售后与供货更稳定,是国内高校、军工科研单位替换 USRP B210 的最优国产化方案。
GNU Radio官方教程系列(九):流和向量
本教程介绍了 流 和 向量 之间的区别。上一篇教程 “打包位 ”介绍了如何将比特流打包成 字节 或 字符 数据类型,以及如何将其解包回比特流。下一篇教程 “层级块和参数 ”演示了如何创建 分层块 以及如何使用 参数 。流在 GNU Radio 中,模块之间可以使用 流 或 向量 进行连接。 流 在每个时间点携带一个样本。 流 生成序列化数据。 流 必须具有数据类型,例如 Float32 或 Byte 。信号源 模块产生一个 复浮点数 32 数据流。该模块在每个时间点的输出包含 1 个复数样本:图中显示,每个时间点都有一个复杂的样本。向量向量 在每个时间点携带多个样本。 向量 并行表示数据。 流 在每个时间点表示一个标量。 向量 在每个时间点表示一个数组。GRC 使用较浅的颜色表示 数据流 ,使用较深的颜色表示 向量 输出:流到向量流程图示例以下示例描述了如何将数据流转换为向量,以及如何将向量转换回数据流。两个复杂的正弦数据流被转换为一个二元向量并显示出来,然后再转换回它们各自的独立数据流。向工作区添加两个 信号源 模块:编辑第二个 信号源 的参数,使其频率为 100,幅度为 0.1,以便在视觉上将其与第一个 信号源区 分开来:点击 “确定” 接受参数。找到 “流到向量” 模块,将其拖入工作区并将其连接到 “信号源” 模块:找到 Vector to Stream 模块,并将三个 QT GUI Time Sink 模块添加到流程图中:“向量转流”模块 会将向量序列化为流。 “向量转流”模块 输出的样本将交错排列。编辑三个 QT GUI 时间接收器 模块的标题,以便彼此区分。首先,编辑连接到频率为 1000、幅度为 1 的 信号源的 模块,将其标题设置为 “信号源 A” :现在编辑连接到第二个 QT GUI 时间接收器, 信号源( “信号源 B” 频率为 100,幅度为 0.1)的 将其标题设置为 :最后,编辑连接到 QT GUI Time Sink Vector to Stream “交错信号源” 模块的 ,将其标题设置为 :运行流程图会显示三个时间接收器: 信号源 B、 信号源 A 、交错信号源 。交错信号源 时间接收器显示来自 信号源 A和信号源 B 的交错样本:向量到流的流程图示例以下示例对向量化数据进行解交错(或序列化),将其转换回两个流。搜索 “向量到流” 模块,将其添加到工作区,并将其连接到 “流到向量” 模块:右键单击并删除连接两个 信号源 模块和 QT GUI 时间接收器 模块的箭头:将两个 Vector to Streams QT GUI Time Sink 模块移动并重新连接到 输出:运行流程图。矢量化样本已重新分离成两个流:下一个教程“ 层级块和参数 ”演示了如何创建 和使用 层级块 。
GNU Radio官方教程系列(八):打包位(比特)
本教程介绍了如何使用 Pack K Bits 模块将位打包成一个字节,以及如何使用 Unpack K Bits 模块将一个字节解包成位。上一篇教程 “数据类型转换 ”介绍了 字符型 和 字节型 数据类型以及如何在数据类型之间进行转换。下一篇教程“ 流和向量 ”介绍了流和向量之间的区别以及如何在流程图中使用它们。开始打包位流程图将 比特 打包成一个字节对于表示二进制数据(而非数字化射频采样)以及使用调制器模块(例如 星座调制器 、 GFSK 调制器 和 OFDM 发射机 )都非常有用。创建一个新的流程图,并将 随机源 模块添加到工作区:点击 “随机源” 。选中时会显示 浅蓝色 边框:按 向上 或 向下 键循环切换不同的数据类型,直到选中 字节 数据类型(以 紫色 输出端口颜色表示):随机源生成的字节最小值为 Minimum ,最大值为 Maximum-1 。在本例中, Minimum = 0 , Maximum = 2 ,因此它将生成二进制 0 和 1。Pack K Bits 模块用于将多个比特并行化(或 打包 )到单个字节中,以表示更大的二进制值。将 Throttle 、 Pack K Bits 、 Char to Float 和 QT GUI Histogram Sink 模块添加到流程图中并连接它们:解释 K 位块的打包Pack K Bits 块接收 K 位,并通过先填充最低有效位 (LSB) 将它们放入一个字节中。在本例中, K=4 。 随机源 首先生成比特 B 0 。该比特将被 数据包 K 位 接收,然后存储在最低有效位 (LSB) 中:[0 0 0 0 0 0 0 B 0]Pack K Bits 生成的第二个比特是 B 1 ,然后 Pack K Bits 根据以下方式存储它:[0 0 0 0 0 0 B 1 B 0 ]按照这个规律,接下来的比特 B 2 和 B 3 将存储为:[0 0 0 0 B 3 B 2 B 1 B 0 ]下图展示了该模块的工作原理:由于 0000B K=4 位已被打包,因此将输出字节 3 B 2 B 1 B 0 ,并开始一个新的字节。该字节的输出值(十进制)为:= (B 3 *2 3) + (B 2 *2 2) + (B 1 *2 1) + (B 0 *2 0)= (B 3 *8) + (B 2 *4) + (B 1 *2) + (B 0)例如,如果:B 0 =0B 1 =1B 2 =0B 3 =1该字节用 00001010 表示,其十进制值为:8 + 0 + 2 + 0 = 10 完成 Pack K Bits 流程图编辑 Pack K Bits 的属性:K: 4四位二进制数可以生成 0 到 QT GUI 顶部直方图接收器 4 2 之间的数字 -1=15 。编辑 属性,并更改以下内容:标题: 4 位垃圾箱数量: 1024x 轴最大值: 16四位二进制数可以生成 0 到 2 之间的数字 4 -1=15 。编辑 QT GUI 底部直方图接收器 属性,并更改以下内容:标题: 1 比特垃圾箱数量: 1024x 轴最大值: 16流程图应如下所示:运行流程图。1 0 位 1 直方图显示值 4 位 和 0 ,而 15 直方图显示值从 到 :解包比特解包操作会将一个 字节 序列化为一串比特。将 “解包 K 位” 模块添加到工作区,并将其连接在 “打包 K 位” 模块和 “字符转浮点数 ”模块之间。编辑 “解包 K 位” 模块的属性,并输入 K: 4 。运行流程图。1 位直方图显示打包的 4 位被解包(序列化)回 0 和 1 的值:解包过程从最低有效位 ( 最高有效位 (MSB) LSB) “打包 K 位” 开始,然后依次进行到 0000B 。在前面的示例中, “解包 K 位” 3 模块生成了一个字节,其位为 2 B 1 B 0 B B 。 B 0 模块的输出结果首先是位 B 1 ,然后是位 B 2 、 和 3 ,字节中剩余的 4 个零位将被忽略。连接模块:QT QUI 时间接收器显示输出:单击鼠标左键并拖动鼠标,即可放大查看:这两个图完全重合。 “解包 K 位” “打包 K 位” “打包 K 位” 模块的输入与 “解包 K 位”模块 模块的输出完全相同。这表明解包和打包 执行的是完全相反的操作。 下一个教程 “流和向量 ”介绍了流和向量之间的区别,以及如何在流程图中使用它们。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
