精华内容
下载资源
问答
  • matlab开发-软决策维特比编码与穿孔。使用Simulink(R)在awgn信道上进行软decison-viterbi解码。
  • matlab开发-软决策维特比编码与穿孔.zip
  • matlab开发-ViterbideCodingMostProbablePath(维特比编码mostProbablePath)。维特比译码,通过给定的状态找到最可能的路径。可以指定结束状态
  • #卷积(维特比编码和解码库#F4GOH安东尼 2015年5月 在Arduino 1.0.6中自由使用此库 安装 要使用CONVOLUTION库: 转到 ,单击“按钮,然后将ZIP文件保存到PC上的方便位置。 解压缩下载的文件。 这将导致一个...
  • 1)作为卷积编码器的输入,并获得相应的代码字。 获得的码字长度为8。 算法: 介绍了所有可能的1位错误(8种可能性),2位错误(28种可能性)和3位错误(56种可能性)。 然后,使用维特比算法对每个结果码字进行解码...
  • 信息论课程的一套代码,附有详细的每一部分的代码说明文档。卷积编码采用自己写的代码,完成了217和319码。在MATLAB2016b环境下编译通过
  • 在发送端根据生成多项式进行卷积码编码,在接收端根据维特比(Viterbi)译码算法进行译码,能够有效抵抗信道噪声的影响,在误码率门限之下可以对传输过程中发生的突发错误进行纠错。 1、编码及译码算法的基本原理 ...

    引言

          卷积码是一种信道纠错编码,在通信中具有广泛的应用。在发送端根据生成多项式进行卷积码编码,在接收端根据维特比(Viterbi)译码算法进行译码,能够有效抵抗信道噪声的影响,在误码率门限之下可以对传输过程中发生的突发错误进行纠错。

    1、编码及译码算法的基本原理

    • 卷积码编码

           卷积码是一种纠错编码,它将输入的k个信息比特编成n个比特输出,特别适合以串行形式进行传输,时延小。卷积码编码器的一般形式如下图所示。

         如下图所示为k=1时的编码框图,k=1也是最常用的一种编码器情形:

    • 译码算法

          卷积码的译码方法有两类:一类是大数逻辑译码,又称门限译码;另一类是概率译码,概率译码又能分为维特比译码和序列译码两种。维特比(Viterbi)译码和序列译码都属于概率译码。当卷积码的约束长度不太大时,与序列译码相比,维特比译码器比较简单,计算速度更快。接下来的译码算法采用的是概率译码中的维特比译码。采用概率译码的一种基本想法是:把已接收序列与所有可能的发送序列做比较,选择其中汉明码距最小的一个序列做为发送序列。

    • 编码及译码算法的Matlab实现

           根据如上所述的编译码基本原理,我们可以在Matlab中进行很方便的仿真,Matlab提供了集成化的函数可供调用,进行仿真,如下所示:

    % 0101随机数,待编码数据
    bits = randi([0 1],256,1);
    % trellis = poly2trellis(7,{'1 + x^3 + x^4 + x^5 + x^6', ...
    %     '1 + x + x^3 + x^4 + x^6'})
    % 产生生成多项式
    gen_math = poly2trellis(7, [171 133]);% 8进制,分别对应相应位置的抽头
    % 卷积码编码
    codeData = convenc(bits,gen_math);
    % Viterbi译码 
    decodeData = vitdec(codeData,gen_math,34,'trunc','hard');
    % 算误码
    err = biterr(decodeData,bits);

    2、编码算法的FPGA实现

          根据卷积码编码的基本原理 ,我们可以根据相应的生成多项式来进行Verilog编码,从而可以很方便的实现卷积码编码的FPGA实现:

    • 顶层代码
    module  convenc(
    	//system signals
    	input	clk, 
    	input	rst_n,
    	input	data_in,
    	output	reg [1:0] data_out     
    );
    reg [6:0] conv_reg;
    
    always @(posedge clk or negedge rst_n) begin
      	if (!rst_n) begin
      		conv_reg <= 7'd0;
      	end
      	else begin
      		conv_reg <= {data_in,conv_reg[6:1]};
      	end
    end  
    
    always @(posedge clk or negedge rst_n) begin
    	if (!rst_n) begin
    		data_out <= 2'd0;
    	end
    	else begin
    	    data_out[1] <= conv_reg[6]^conv_reg[5]^conv_reg[4]^conv_reg[3]^conv_reg[0];//o171
    		data_out[0] <= conv_reg[6]^conv_reg[4]^conv_reg[3]^conv_reg[1]^conv_reg[0];//o133
    	end
    end
    endmodule
    • 测试代码
    `timescale 1ns/1ps;
    module tb();
    
    reg clk;
    reg rst_n;
    reg data_in;
    wire [1:0] data_out;
    reg bits[255:0];
    integer out_file;
    integer i;
    
    convenc demo(
    	.clk(clk),
    	.rst_n(rst_n),
    	.data_in(data_in),
    	.data_out(data_out)
    	);
    
    initial
    begin
    	clk = 1'b1;
    	rst_n = 1'b1;
    	#5 rst_n = 1'b0;
    	#5 rst_n =1'b1;
    	$readmemb("F:/FPGA_DSP/Viterbi/bits.txt",bits);
    	out_file = $fopen("F:/FPGA_DSP/Viterbi/result.txt","w");//获取文件句柄
    	for(i = 0; i <= 255; i = i + 1)begin
    	        data_in = bits[i];
    	        #10;
    	        $fwrite(out_file,"%b\n%b\n",data_out[1],data_out[0]);
    	end
    end
    always #5 clk = ~clk;
    endmodule
    • 仿真结果

    3、维特比译码(Viterbi)算法的FPGA实现

         维特比译码(Viterbi)算法在数学原理上是比较复杂的,从理解算法到实现需要做大量的工作,但是Xilinx的Vivado工具给我们提供了Viterbi decoder IP核,我们可以很方便地调用这个IP核进行算法的FPGA实现和落地。

    • Viterbi decoder IP核输入输出数据格式

           Viterbi decoder IP核的接口是基于AXI-Stream协议的,在之前的文章中已经有提及AXI-Stream协议的握手过程,如果有不懂的可以去看前面的文章,下面主要介绍一下该IP和输入输出数据的基本格式组成:

           输入数据:

           当IP核作为接收输入数据的时候,扮演的是从机的角色,输入数据的格式如下图所示,下图对应的是编码速率为2的情况。如果编码速率为N,那么数据的位宽相应为N*8。

          当IP核配置为硬判决时,输入数据位宽为1,其余位用0补齐, 当IP核配置为软判决时,输入数据位宽为3-5,其余位用0补齐,DATA_IN1对应高位,DATA_IN0对应低位。

          输出数据:

          IP核的译码输出数据总是1位,格式如下图所示。

          

         最低位为译码数据,其他数据可以不做深入了解。

         另外除了待译码数据的输入端口和译码数据输出端口外,该IP核还可以进行误码率(BER)的计算,其余端口位误码率计算配置端口和结果输出端口,具体详情请参考官方手册pg027。

    • IP核生成流程

         Vivado软件为我们提供了Viterbi译码IP核,可以进行图形化配置然后进行调用和使用,配置参数要与编码过程中的相关参数严格对应,具体过程如下所示:

           在图形化配置IP核完成后,我们提取相应的网表文件在Modelsim环境下进行了仿真,如何在Modelsim环境下仿真Vivado IP核我们在前面也有提及,如有不懂的也可翻阅前面的文章进行学习,相关测试程序如下。

    `timescale 1 ns / 1 ps
    module dec_tb ();
    glbl glbl();
    reg  aclk;
    reg aresetn;
    
    reg [15:0]s_axis_data_tdata;
    reg s_axis_data_tvalid;
    wire s_axis_data_tready;
    
    wire [7:0]m_axis_data_tdata;
    wire m_axis_data_tvalid;
    reg m_axis_data_tready;
    
    reg [15:0] s_axis_dstat_tdata;
    reg s_axis_dstat_tvalid;
    wire s_axis_dstat_tready;
    
    wire [15:0]m_axis_dstat_tdata;
    wire m_axis_dstat_tvalid;
    reg m_axis_dstat_tready;
    
    reg codeData[511:0];
    reg [9:0]i;
    reg [9:0]j;
    integer out_file;
    initial begin
    	aclk = 1'b1;
    	aresetn = 1'b1;
    	#5 aresetn = 1'b0;
    	#5 aresetn = 1'b1;
    	$readmemb("F:/FPGA_DSP/Viterbi/codeData.txt",codeData);
    	out_file = $fopen("F:/FPGA_DSP/Viterbi/decodeData.txt","w");//获取文件句柄
    end
    
    always #5 aclk = ~aclk;
    
    //送数据
    always @(posedge aclk or negedge aresetn) begin
    	if (!aresetn) begin
    		s_axis_data_tvalid <= 1'b0;
    		s_axis_data_tdata <= 16'd0;
    		i <= 9'd0;
    		j <= 9'd1;
    	end
    	else if (s_axis_data_tready) begin
    		if(i <= 9'd510)begin
    			s_axis_data_tvalid <= 1'b1;
    			s_axis_data_tdata <= {7'd0,codeData[j],7'd0,codeData[i]};
    			i <= i + 2;
    			j <= j + 2;
    		end
    		else
    			s_axis_data_tvalid <= 1'b0;
    	end
    end
    //取数据
    always @(posedge aclk or negedge aresetn) begin
    	if (!aresetn) begin
    		m_axis_data_tready <= 1'b1;
    	end
    	else if (m_axis_data_tvalid) begin
    		$fwrite(out_file,"%b\n",m_axis_data_tdata[0]);
    	end
    end
    //----------- Begin Cut here for INSTANTIATION Template ---// INST_TAG
    viterbi_0 your_instance_name (
      .aclk(aclk),                                // input wire aclk
      .aresetn(aresetn),                          // input wire aresetn
      //接收数据时为从设备
      .s_axis_data_tdata(s_axis_data_tdata),      // input wire [15 : 0] s_axis_data_tdata
      .s_axis_data_tvalid(s_axis_data_tvalid),    // input wire s_axis_data_tvalid
      .s_axis_data_tready(s_axis_data_tready),    // output wire s_axis_data_tready
      //发送数据时为主设备
      .m_axis_data_tdata(m_axis_data_tdata),      // output wire [7 : 0] m_axis_data_tdata
      .m_axis_data_tvalid(m_axis_data_tvalid),    // output wire m_axis_data_tvalid
      .m_axis_data_tready(m_axis_data_tready),    // input wire m_axis_data_tready
      //BER测量
      .s_axis_dstat_tdata(s_axis_dstat_tdata),    // input wire [15 : 0] s_axis_dstat_tdata
      .s_axis_dstat_tvalid(s_axis_dstat_tvalid),  // input wire s_axis_dstat_tvalid
      .s_axis_dstat_tready(s_axis_dstat_tready),  // output wire s_axis_dstat_tready
      .m_axis_dstat_tdata(m_axis_dstat_tdata),    // output wire [15 : 0] m_axis_dstat_tdata
      .m_axis_dstat_tvalid(m_axis_dstat_tvalid),  // output wire m_axis_dstat_tvalid
      .m_axis_dstat_tready(m_axis_dstat_tready)  // input wire m_axis_dstat_tready
    );
    endmodule
    

    仿真波形:

    展开全文
  • matlab实现的维特比译码,已用数字通信第二版实例验证通过
  •   ... 对维特比译码的接触很早就开始了,也想过要写一篇总结...编码约束度和监督位数量对维特比译码的性能是如何产生影响的,等等……直到前些天看到这篇来自MIT的数字通信系统课程的教案,诸多的疑惑才豁然开朗。这

    本文转载自:https://www.cnblogs.com/ncdxlxk/p/9240938.html

     

    了解更多,点击:更多通信知识

    对维特比译码的接触很早就开始了,也想过要写一篇总结性的文章,但无奈心中一直有几个疑团没能得到合理的解答。比如什么时候开始进行回溯译码比较合适?维特比译码的性能相比分组码等其他编码的译码性能究竟好在哪里,如何来评估?编码约束度和监督位数量对维特比译码的性能是如何产生影响的,等等……直到前些天看到这篇来自MIT的数字通信系统课程的教案,诸多的疑惑才豁然开朗。这篇文章将通信理论中的维特比译码的前前后后讲的非常的全面,是我读到的关于维特比译码最好的文章(没有之一)!特此翻译过来,一来加深自己的理解,二来供大家学习讨论~

     

    本文翻译自 MIT 6.02 DRAFT Lecture Notes, 2012

    CHAPTER 8:Viterbi Decoding of Convolutional Codes

     

    卷积码的构造和编码方法已经在前一章中描述过,本章将描述一种优雅高效的卷积码译码方法,它避免我们对N个比特序列的2^N种可能的路径组合做穷举。该方法由Andrew Viterbi 发明,并以他的名字命名。

     

    面临的问题

    在接收端,我们有一组对应于发射监督比特的电压采样序列。为简单并不失一般性,我们将假设接收端获得了最佳采样点(或者一组采样集的均值对应一个监督位),通过与阈值比较判为“0”或“1”(解映射),并将判决结果传递给译码器。

    现在我们有了一组接收比特序列,对应于卷积编码输出的监督比特。在没有关于采样点和译码器其它信息的情况下,译码过程被称为硬判决译码(“硬译码”)。 如果提供给译码器以采样点的“模拟”信息(以数字化形式,通过模数转换量化),我们称该过程为软判决译码(“软译码”)。

    维特比译码器适用于任何一种情况。直观讲,由于硬判决译码会对比特是0还是1做出早期判决,因此在数字化过程中丢失了部分信息。硬判决将在接收的比特序列中引入更多的误码,因为它可能做出错误的判决,特别是对于接近阈值的电压而言。尽管硬判决译码仍然可以产生最可能的发送序列,然而由于早期数字化中引入额外的错误,其纠错能力将小于软判决译码。但硬判决译码在概念上更容易理解,因此我们将从此开始,然后再介绍软判决译码。

     

    图1

     

    如前一章所述,网格为了解卷积码的译码过程提供了一个很好的框架(如图1)。假设我们有表述一组码的整个网格,并且接收到一串数字比特(或电压样点)。如果接收没有错误,那么定会有一些网格路径与接收到的序列完全匹配,该路径对应于发射监督比特(具体地说是在状态转移的过程中产生的发射监督比特)。从网格的每个节点发出,往上的路径对应于“0”bit,往下的路径对应于“1”bit,据此很容易译得原始编码信息。

    当有误码发生时,我们可以做什么?如前所述,找到最可能发送的消息序列是有吸引力的,因为它最大限度地减少了译码的误码率。如果我们能够想出一种方法来度量从一个状态到下一个状态所引入的错误量,那么我们可以沿着一条路径累积这些错误量,并得出沿着该路径的总错误量。然后,以此得到的总错误量最小的路径就是我们想要的路径,并且发送的消息序列可以通过上面解释的状态转移关系方便地确定。

    我们需要一种方法来收集通过网格状态时发生的任何错误,以及无需遍历整个网格的路径导航方式(即无需穷举所有可能路径也能找到累积错误最小的那条路径),维特比译码器解决了这些问题。动态规划是一个解决优化问题的普遍方法,维特比译码就是其中一例。在本课程的后面,我们将在网络路由中应用相似的概念,可以在多跳网络中找到好的路径。

     

    维特比译码器

    译码算法使用两个度量:分支度量(branch metric,BM)和路径度量(path metric,PM)。分支度量计算的是发射和接收内容之间的“距离”,它是为网格中的每条分支路径定义的。在硬判决译码中,给出一组已经数字化的接收监督比特,分支度量就是监督比特预测值和接收监督比特之间的汉明距离。图2展示了一个示例,其中接收的位为00,对于每个状态转移,分支路径上的数字显示该转移的分支度量。其中有两条路径的分支量度为0,对应于汉明距离为0的唯一状态和转移关系,其他非0分支量度对应于存在位错误的情况。

     

    图 2

     

    路径度量值与网格中的状态相关联(即与整个路径的每个节点有关)。对于硬判决解码,它对应于网格中从初始状态到当前状态的最可能路径与接收监督比特序列间的汉明距离。“最有可能”是指在计算从初始状态到当前状态之间的所有可能路径度量后,取汉明距离最小的那条。具有最小汉明距离的路径使误码最小化,并且当误码率较低时具有最大似然度。

    维特比算法的关键点在于,接收机可以使用分支度量和先前计算的状态路径度量递推地计算当前状态的路径度量。

     

    计算路径度量

    假设接收机已经在时刻i计算好每个状态s的路径量度PM[s,i](设卷积码的编码约束度为K,则状态数为2^(K-1))。在硬判决译码中,PM[s,i]的值是在接收监督比特与最可能发送的消息进行比较时得到的差错比特总数(通常我们将状态“00”作为起始状态)。

    在时刻i的所有可能状态中,最可能的状态是具有最小路径度量的状态。如果具备最小路径度量的状态不止一个,那它们拥有相等的可能性。

    现在,我们如何确定时刻i+1下每个状态s的路径度量PM[s,i+1]呢?要回答这个问题,首先要注意的是,对于i+1时刻的状态s,它必须由i时刻的两种可能状态中的一个中转移而来。这两个之前状态记为α和β,并且对于给定的状态s,它们是固定的。实际上α和β仅由卷积码的编码约束度决定,与生成多项式无关。图2显示了每个状态的之前状态(箭头的另一端),该例中,对于状态00,α= 00 ,β= 01;对于状态01,α= 10 ,β= 11。

    任何使得发射机在i+1时刻处于状态s的信息序列必定使得发射机在i时刻位于状态α或β。例如,在图2中,在时刻i+1时到达状态01,必定符合以下两点之一:

    1. 发射机在时刻i位于状态10,且第i个信息比特为0。在这种情况下,发射机输出监督位11。由于接收比特为00,因此将产生2位误码,新的状态路径度量PM[01,i+1] = PM[10,i] + 2。

    2. 发射机在时刻i位于状态11,且第i个信息比特为0。在这种情况下,发射机输出监督位01。由于接收比特为00,因此将产生1位误码,新的状态路径度量PM[01,i+1] = PM[11,i] + 1。

    通过上面直观的分析,我们看到:

     (式1)

    其中α和β为上一时刻的可能状态。

    在译码算法中,重要的是记录最小汉明距离对应的那条路径,因为我们需要通过跟踪所记录的路径,遍历从最终状态到最初状态的整条路径,然后将估计比特倒序,最终得到最可能的消息。

     

    寻找最大似然路径

    现在我们可以来描述译码器是如何找到最大似然路径了。初始时,状态00代价为0,其它2^(k-1)-1个状态代价为正无穷(∞)。

    算法的主循环由两个主要步骤组成:首先计算下一时刻监督比特序列的分支度量,然后计算该时刻各状态的路径度量。路径度量的计算可以被认为是一个“加比选”过程:

    1.将分支度量与上一时刻状态的路径度量相加。

    2.每一状态比较达到该状态的所有路径(每时刻每个状态只有两条这样的路径进行比较,因为只有两条来自前一时刻状态的分支)。

    3.每一状态删除其余到达路径,保留最小度量的路径(称为幸存路径),该路径对应于错误最少的路径。

    图3显示了维特比译码器从一个时刻到下一个时刻的译码过程。这个例子显示接收到的位序列为11 10 11 00 01 10,以及接收器如何处理它。第四部分显示了具有相同路径度量四个不同状态,在这个阶段,通向这四个状态的任一路径都是最可能发送的比特序列(它们都具有度量为2的汉明距离)。最下面的部分只显示幸存路径的情况,幸存路径是有可能成为最大似然路径的路径。还有很多其他路径可以被删除,因为它们不可能有状态回溯回来。维特比译码器之所以能被实际应用,是因为幸存路径的数量远远小于网格中所有可能路径的总数。

     

    图 3

     

    关于维特比译码器的另一个重点是,未来的知识将帮助它打破任何关系,甚至可能导致在特定时刻步骤被认为“最可能”的路径发生改变。图4继续图3中的例子,直到所有接收到监督比特序列都被译码,得到最有可能的发送消息路径,其具有两位错误。

     

    图 4

     

    软判决译码

    硬判决译码通过将接收到的电压信号与阈值进行比较,数字化后将其传递给译码器进行译码。结果,我们失去了部分信息:电压为0.500001时数字化结果的可信度肯定比电压为0.999999时低得多,但两者都被视为“1”,且译码器现在以相同的方式对待它们,尽管0.999999比其他值更可能是“1”。

    软判决译码(有时也称为“软输入维特比译码”)建立在这一观察上,它在译码之前不会数字化输入样本。相反,它使用具备连续性的模拟样本(经采样量化)作为译码器的输入。例如,如果期望的发射监督位为0并且接收到的电压为0.3V,那么我们可以使用0.3(或0.32或某些此类函数)作为该位的值,而不是数字化它。

    一个恰当的度量计算方式是求接收电压和预期电压之差的平方,其中的技术原理将在后面讲到。如果卷积码产生p个监督比特,它们对应的模拟样本是v = v1,v2,...,vp,可以按如下方式构建软判决分支度量:

           (式2)

    u = u1,u2,...,up是预期的p个监督位(为0或1)。图5显示了p=2时,当u为00时的软判决分支度量。

    图5 

     

    软判决译码算法与前述硬判决译码的译码过程相同,除了分支度量不再是整数汉明距离而是一个正实数。

    事实证明,当信道经受加性高斯噪声时,该软判决度量与正确译码的概率密切相关。首先,我们来看1个监督比特位时的简单情况(可直接扩展为其它一般情况),假设接收机获得的第i个监督位对应的数据为电压vi(在硬判决译码中,根据vi是小于还是大于0.5,它将判为为0或1),当发送信息位为ui时(0或1),vi将被接收的概率是多少?当信道噪声为零均值加性高斯噪声时,此事件的PDF由下式给出:

                    (式3)

    其中,ui=0时,di=vi^2;ui=1时,di=(vi-1)^2。

    该PDF的对数似然函数与-di^2成比例。一组发送码序列U = ui,u2,...,up得到接收序列V = v1,v2,...,vp,其联合概率密度函数为各独立样点PDF的乘积。该路径的对数PDF为各独立样点对数似然函数的和,且与-∑i(di^2)成比例。

    但这恰恰是我们在公式2中定义的分支度量的负值,维特比译码器最小化不同的可能路径度量,而最小化路径度量等同于最大化对数似然度!

    这意味着软判决译码器产生与接收的电压序列相对应的最大似然路径。

    我们之所以选择平方和作为公式2的分支度量,是由于其与对数PDF的直接关系。不同的噪声分布(除了高斯)可能需要不同的软解码分支度量,以获得与正确译码PDF类似的联系。

     

    实现更高和更精细的匹配速率:打孔

    按目前为止所描述的,卷积码的最大码率为1/r,其中r是由卷积码1比特输入生成的监督比特数。但是如果我们想要一个大于1/2的码率,或者一些在1/r和1/(r+1)之间的码率呢?

    一种叫做打孔的通用技术给我们提供了一种方法。其思想很简单:编码器不发送每一个监督位,而是在输出流上进行打孔,仅发射由收发双方约定好的部分码流。例如,对于1/2卷积编码,一个人通过在编码输出上每3bit删除一个特定位,原本每3bit需要发送6bit监督数据,而现在只需要发送4bit,由此得到3/4码率的编码输出。

    在这个例子中,设卷积编码器为1/2编码,在打孔前,需要发送p0[0]p1[0]p0[1]p1[1]p0[2]p1[2],在打孔之后则只发送p0[0]p1[0]-p1[1]p0[2]-,其中-表示被删除的比特。

    在译码器中,当使用打孔编码时,被删除的监督位不参与分支度量的计算,除此之外的译码过程与以前相同。我们可以把每一个缺失的监督位看作一个空白(“-”),运行译码器时只需跳过空白。

     

    编码和译码的实现复杂度

    关于卷积编码器和维特比译码器的时间和空间复杂度,我们必须回答两个重要的问题:

    1. 编码器需要多少状态和存储空间?

    2. 译码器需要多少时间完成译码?

    第一个问题容易回答:在编码器端,需要的存储空间与编码约束度K成线性关系;需要的时间与消息长度n成线性关系。编码器比维特比译码器的实现要容易的多。译码器需要的时间取决于编码约束度K和接收序列长度(与n成线性关系)。在每个时刻,译码器需要计算转移至每一状态的两条分支路径,共2^(K-1)个状态,需要进行2^K次比较,因此译码n比特信息的时间复杂度为O(n*2^K)。

    此外,按目前所描述的来看,我们只能在最末端解码消息的第一位。有一点需要表明,虽然未来的知识是有用的,但假设编码约束度为6,第1000时刻的比特不太可能会改变我们对第1时刻比特的译码结果。事实上,在实践中,译码器计算长度一旦满足编码约束度的几倍就可以开始译码。实验数据表明,无论监督比特流对应于消息比特有多长,5·K的消息比特时间(左右)是一个合理的译码窗口。

     

    设计优秀的卷积码

    到此,人们可能自然地会想到一个问题,“什么样的生成多项式能促成一个优秀的卷积码?换句话说,是否有系统的方法来产生优秀的卷积码?或者,给定两个卷积码,是否有一种方法来分析它们的生成矩阵,并确定它们在接下来的过程中如何相互关联并执行?了解了这些,使得我们能在一个有噪声的信道上以尽可能高的速率进行通信。

    原则上,许多因素决定了卷积码的有效性。人们期望卷积码纠正错误的能力取决于编码约束度K,因为编码约束度越大,任何给定的消息比特对监督位的贡献程度越大,对比特错误的复原能力也就越大。人们也希望随着监督比特数量的增加,错误的恢复能力会更高,因为这对应于一个较低的码率(更多的冗余)。最后同样重要的一点是,生成多项式的系数在决定卷积码的性能方面确实有一定的作用。

    幸运的是,有一个度量,称为卷积码的自由距离,它囊括了这些衡量维度,并对卷积码的纠错能力起主要决定作用。

     

    自由距离

    由于卷积码是线性的,因此所有我们所学的线性码都适用于这里。特别是,任何线性码的汉明距离,即任意两个有效码字之间的最小汉明距离,等于具有最小码重的非零码字中1的个数。一个码字的码重为其所含1的个数。

    对卷积码而言,任意两个有效码字之间的最小汉明距离称为自由距离。具体而言,一个卷积码的自由距离等于全零路径与从状态00重新回到状态00的最小非零路径之间的路径度量之差。图6举例说明这一概念。

     

    图 6

     

    在这个例子中,自由距离是4,它需要8个输出位才能回到正确的状态,所以如果数据块从第一个监督位开始,人们期望这个卷积码每8bit数据块能够纠正(4-1)/2 = 1位错误。事实上,这种纠错能力基本上与(8,4,3)二维奇偶监督码相同。

    注意,这个例子中的自由距离是4,而不是5:从初始00状态回到状00态之间最小的非零路径如下:00→10→11→01→00,相应的路径度量值变化如下:0→2→2→3→4。在下一节中,我们将发现生成器的一个小的改变(如以101替换110),也将大大影响卷积码的性能。

    如果是以相同方式定义的,为什么我们要重新定义一个“自由距离”,而不是也把它叫做汉明距离?原因是,汉明距离为D的任何编码(无论是否为线性码),都可以纠正任何(D-1)/2位形式的错误。如果我们把同样的概念应用于卷积码,我们将会以为我们可以纠正固定位数的错误,如在上例中我们可以纠正任何单比特错误。

    现在,卷积码产生无边界的比特流,这明显不同于分组码。(D-1)/2不太适合于描述卷积码真正的纠错性能。当这些错误离得足够远时,卷积码(使用Viterbi译码)可以纠正t = (D-1)/2位形式的错误,所以我们使用的是自由距离。从某种意义上说,在一个间隔很近的突发块中,错误可以连续发生,只要没有超过t个错误出现,译码器就可以纠正它们。

     

    选择好的卷积码

    自由距离概念还提供了一种构造良好卷积码的方法。给定译码资源预算(如硬件资源),首先为编码约束度K确定一个边界,然后,根据最大速率选择r的上限。给定一个具体的K和r,就已经把生成多项式的选取方案限定在一定范围内了。人们可以编写一个程序,遍历所有可能的生成多项式组合,计算自由距离,并选择最大自由距离的卷积码。卷积码完全通过指定生成多项式来产生(如果列出生成多项式,则K和r都是隐含在其中的了)。

     

    比较编码的纠错性能

    这一部分讨论如何比较不同编码的纠错性能,并评估在不同控制条件下实现不同编码的仿真结果。本节有两个目标:第一,从“最佳实践意义”角度比较不同编码器,并讨论常见的陷阱;第二,比较某些特定的卷积码和二维奇偶监督码,并讨论某些编码器比其它编码器执行得更好的原因。

    有两个衡量标准:第一是译码后的误码率(BER),有时也称为译码错误概率;第二个是码率。对于这两种度量,我们关心它们如何随信道参数的变化而变化,例如BSC中的ε值(即信道的底层比特差错概率)或信道上的噪声程度(对于加性高斯噪声信道,我们将在下一章中详细描述)。

    这里,我们只关注编码的译码后误码率。

     

    基于BSC的译码误码率

    对于BSC,变量是ε(译者注:BSC全称为二元对称信道,这里BSC的ε表示译码前的错误比特率),当ε变化时,我们关心不同的编码器将会如何表现(以BER衡量)。图7显示了几个不同的线性二维奇偶监督码和卷积码的译码误码率随BSC错误率ε变化的情况。

     

    图 7

     

    图中显示,码率为1/3的二维奇偶监督码在高ε时表现得最为稳健,而两个1/2码率的卷积码在其它误码率下表现很好。图中同样可以看出,(7,4)和(15,11)汉明码要劣于其它编码。

    这些结论的问题是,他们没有考虑到编码的编码效率,其中一些编码的开销要比其他编码高很多。这样,如图7曲线所示译码误码率随ε的变化,将具有相同码率的编码在一起比较才有意义。因此,可以将(8,4)二维奇偶监督码与其他三个卷积码进行比较,并得出以下结论:

    1. 两个卷积码(3,(7,5))(生成多项式为(111, 101))和(4,(14,13))(生成多项式为(1110, 1101))表现最好,它们明显优于(3,(7,6))卷积码(我们从Bussgang关于如何选取好的卷积码的文章获得这些码)。(3,(7,5))和(4,(14,13))这两个码(自由距离分别为5和6)表现优异的原因是它们具有比(3,(7,6))(自由距离为4)更大的自由距离。

    2. 有趣的是,结果显示自由距离为5的(3,(7,5))表现要强于自由距离为6的(4,(14,13))。其原因是,在(3,(7,5))下,从状态00返回到状态00的网格边数仅为3,对应于一组6个连续编码比特。相关的状态转换是00→10→01→00,相应的路径度量为0→2→3→5。相比之下(4,(14,13))虽然拥有大小为6的自由距离,但它需要经过4条网格边来实现000→100→010→001→000的状态转移,意味着在长度为8的滑动窗内它能纠正2bit内的错误。

    此外,从5到6(偶数)的自由距离的增加并不能提高编码的纠错能力。

    3. 对于(8,4)二维奇偶监督码和(3,(7,6))卷积码,译码后的BER大致相同。其原因是该K=3的卷积码的自由距离为4,这意味着它可以在与(8,4)二维奇偶监督码长度相似的块上纠正1个比特错误。直观地说,这两个方案的监督位利用了相同长度的历史信息。在二维奇偶监督码情况下,行监督位来自两个连续的消息位,而列监督位则来自间隔一位的两个消息位,同时我们也发送消息位。所以我们用编码约束度K=3的卷积码来与之比较,证明(3,(111,110))不是一个好的卷积码。

    4. (7,4)汉明码类似于(8,4)二维奇偶监督码,但它具有更高的码率(4/7对1/2),这意味着它用更低的开销实现了相同的纠错功能。因此,可以得出结论,它是比(8,4)二维奇偶监督码更好的编码。

    但是,如何比较不同码率下的译码误码率呢?我们需要一种方法来衡量不同码率编码的冗余量。要做到这一点,我们需要改变模型来解释物理(模拟)层上发生的情况。处理这个问题的一般方法是使用信噪比(SNR)作为控制变量(X轴),发送信号通过高斯噪声干扰的信道。下一章详细地研究了这个噪声模型,这里我们只描述在这个模型下比较编码性能时所获得的基本直觉和结果。该模型对于理解软判决译码的好处也是必不可少的,因为软译码直接使用接收的电压样本作为译码器的输入,而无需先对每个样本进行数字化。通过比较软判决译码和硬判决译码,能观察到增加了多少的增益。

     

    高斯噪声模型和Eb/N0的概念

    考虑一组n bit长的信息。我们有两个编码:C1码率为k/n1,C2码率为k/n2,设n2>n1。因此,对于k bit信息,当使用C1编码时,我们发射n1位,当使用C2编码时,发射n2位。显然,使用C2消耗更多的资源,因为它需要在信道上发送更长的时间。

    衡量C1资源消耗的一个简单方式是运行一个实验,将位1映射为特定电压V1,将位0映射为特定电压V0。由于在下章中将会讲到的一个原因,与译码有关的是V1和V0之间的电压差,因此我们假设这两个电压中值为0。为方便起见,设V1=sqrt(Es),V0=-sqrt(Es),Es为每个样点的能量。该能量(或者说功率)与所使用的电压的平方成比例。

    现在我们使用C1编码,k bit消息被编码成n1 bit。先假定每一编码比特以一个电压样点发送(为简化描述),则每一位的能量Eb=(n1/k)*Es。同样对于C2编码,每一位的能量Eb=(n2/k)*Es。在高斯噪声信道中,每一个电压样点被干扰成具备一定方差的高斯分布,该方差即代表噪声的大小(方差越大,噪声越大,BSC的误码率ε也就会越大)。因此,适合用于比较不同编码码率下的译码误码率的x轴变量应为Eb/N0,它表示每个信息比特的能量与信道高斯噪声能量之比。

     

    图 8

     

    图8展示了模拟不同具有代表性的Eb/N0值的高斯信道的性能实验结果,实验中的每一个数据点都是统计200万个信息比特在一个噪声信道上编码和传输的结果。最上面的曲线显示未编码时的误码率。X轴为Eb/N0,以dB为单位。y轴显示对数刻度上的误码率。从结果看,这些点值得注意:

    1. 好的卷积码明显优于汉明码和二维奇偶监督码。

    2. 软判决译码明显优于硬判决译码。对于相同的译码误码率,软解码相对于硬译码有2至2.3 dB的增益,即硬译码将不得不通过增加信噪比(1.6倍左右)来实现与软译码相同的译码误码率。

     

    总结

    从一开始作为影响力巨大的卷积码的译码方法,到现在维特比译码器已经成为工程系统和各领域中应用最广泛的算法之一。现代磁盘驱动器通过PRML技术来加速访问;语音识别系统,自然语言系统,以及各种通信网络使用此方法或其变种。

    事实上,用更现代的观点描述,可将本课中描述的软判决译码技术看作在隐马尔科夫模型(HMM)中寻找最大似然的状态集路径。一些基本现象被建模为一个马尔可夫状态机,它的状态之间有一定概率的转换。我们观测到被噪声污染的各状态,并将观测结果拼接起来,以确定最可能的状态转移序列。事实证明,维特比解码器是解决这类问题的极好起点(有时也是完备解)。

    另一方面,尽管有着不可否认的成功,维特比译码并不是解卷积码的唯一方法。一方面,它确实需要穷举每个状态,因此它的计算复杂度与编码约束度K呈指数关系。当K很大时,可以使用其他解码方法,例如BCJR或Fano的序贯译码方案。

    卷积码本身在有线和无线链路上非常流行。它有时被用作外部块纠错码的“内部码”,但也可自己作为外部纠错码使用。Turbo码是目前实践中使用性能最高的译码器之一,卷积码即可作为其内部组件。

     

     

    展开全文
  • Matlab实现 维特比译码

    千次阅读 2021-06-12 15:43:12
    利用matlab实现维特比译码 话不多说,直接展示代码 首先是利用卷积码进行编码 然后加入awgn噪声 最后利用维特比译码 绘制误比特率图像 卷积码进行编码 %% 初始化参数 N = 1000; %序列长度 code_in = randi(2,1,N)...

    利用matlab实现维特比译码

    话不多说,直接展示代码

    1. 首先是利用卷积码进行编码
    2. 然后加入awgn噪声
    3. 最后利用维特比译码
    4. 绘制误比特率图像

    同时我也上传所有的代码文件
    Matlab 实现维特比译码

    在使用时,首先运行encode,对于随机产生的N=1000个比特流进行卷积编码
    然后直接运行show errorbit,它会调用维特比译码的函数viterbi_hard(),同时绘制出在加入awgn白噪声后,不同信噪比下的误比特率。

    卷积码进行编码
    生成向量为 g1: [1,1,1], g2: [1,0,1]

    encode

    %%  初始化参数
    N = 1000; %序列长度
    
    code_in = randi(2,1,N)-1;
    %code_in = [1,1,0,1,1,1,0,0,1];
    N =  length(code_in);
    g1 = [1,1,1];
    g2 =  [1,0,1];
    
    %% 计算 卷积码
    %x_g1
    
    x0 = g1(1) * code_in;
    x1 = g1(2) * code_in;
    x2 = g1(3)* code_in;
    x = xor( x0(2:N), x1(1:N-1) );
    x = [ x0(1), x, x1(N) ];
    x3 = xor(x(3:N+1),x2(1:N-1));
    x_g1 = [x(1:2) ,x3, x2(N) ];
    %x_g2
    x0 = g2(1) * code_in;
    x1 = g2(2) * code_in;
    x2 = g2(3)* code_in;
    x = xor( x0(2:N), x1(1:N-1) );
    x = [ x0(1), x, x1(N) ];
    x3 = xor(x(3:N+1),x2(1:N-1));
    x_g2 = [x(1:2) ,x3, x2(N) ];
    
    %% 合并两路
    
     x = zeros(1,size(x_g1, 2)+size(x_g2, 2));
     x(1:2:end) = x_g1;
     x(2:2:end) = x_g2;
    x = x(1:length(x) - 4);
    
    

    维特比译码
    将状态转移图存在graph中
    decode

    function m = viterbi_hard(x)   
    %% 
    % a 00
    % b 10
    % c 01
    % d 11
    %x = [ 1 1 0 1 0 1 0 0 0 1];
    sizex = size(x);
    s = sizex(2)/2;
    x = [0,0,0,0, x];
    % to record the value 
    val_a = 0;
    val_b = 0;
    val_c = 0;
    val_d = 0;
    
    % graph       aa0    ab1    bc0   bd1    ca0     cb1   dc0   dd1      
    gra =          [ 0,0;    1,1;    1,0;    0,1;    1,1;    0,0;    0,1;    1,0  ];
    
    % to record route 
    ma = zeros(1,s+2);
    mb = zeros(1,s+2);
    mc = zeros(1,s+2);
    md = zeros(1,s+2);
    %% stage 1 and 2
    
    val_a = val_a + dis(gra(1,:), x(1:2));
    ma(1)=0;
    val_b = val_b + dis(gra(2,:), x(1:2));
    mb(1)=1;
    
    mc = mb;
    md = mb;
    val_c = val_b + dis(gra(3,:), x(3:4));
    mc(2)=0;
    val_d = val_b + dis(gra(4,:), x(3:4));
    md(2)=1;
    
    val_a = val_a + dis(gra(1,:), x(3:4));
    ma(2)=0;
    val_b = val_b + dis(gra(2,:), x(3:4));
    mb(2)=1;
    
    for i = 3:s+2
    %     val_a_t =val_a;
    %     val_b_t =val_b;
    %     val_c_t =val_c;
    %     val_d_t =val_d;
    %     tempa = ma;
    %     tempb = mb;
    %     tempc = mc;
    %     tempd = md;
        % for val_a
            if val_a + dis(gra(1,:), x(2*i-1:2*i)) >= val_c + dis(gra(5,:),x(2*i-1:2*i))
                tempa = mc; 
                val_a_t = val_c + dis(gra(5,:),x(2*i-1:2*i));
                tempa(i)=0;
            else
                val_a_t = val_a + dis(gra(1,:),x(2*i-1:2*i));
                tempa = ma;
                tempa(i)=0;
            end
          %for val_b
             if val_a + dis(gra(2,:), x(2*i-1:2*i)) >= val_c + dis(gra(6,:),x(2*i-1:2*i))
                tempb = mc; 
                val_b_t = val_c + dis(gra(6,:),x(2*i-1:2*i));
                tempb(i)=1;
            else
                val_b_t = val_a + dis(gra(2,:),x(2*i-1:2*i));
                tempb = ma;
                tempb(i)=1;         
             end
            
             %for val_c 
                if val_b + dis(gra(3,:), x(2*i-1:2*i)) >= val_d + dis(gra(7,:),x(2*i-1:2*i))
                tempc = md; 
                val_c_t = val_d + dis(gra(7,:),x(2*i-1:2*i));
                tempc(i)=0;
                else
                val_c_t = val_b + dis(gra(3,:),x(2*i-1:2*i));
                tempc = mb;
                tempc(i)=0;
                end
                
          %for val_c
                if val_b + dis(gra(4,:), x(2*i-1:2*i)) >= val_d + dis(gra(8,:),x(2*i-1:2*i))
                tempd = md; 
                val_d_t = val_d + dis(gra(8,:),x(2*i-1:2*i));
                tempd(i)=1;
            else
                val_d_t = val_b + dis(gra(4,:),x(2*i-1:2*i));
                tempd = mb;
                tempd(i)=1;
                end
        val_a =val_a_t;        
        val_b =val_b_t;
        val_c =val_c_t;
        val_d =val_d_t;
        ma = tempa;
        mb = tempb;
        mc = tempc;
        md = tempd;        
    end
    
    if val_a <= val_b
        m = ma;
        t = val_a;
    else
        m = mb;
       t = val_a;
    end
    
    if val_c <= t
        m = mc;
        t =val_c;
    end
    
    if val_d <= t
        m = md;
        t = val_d;
    end
    %% 去掉最开始的00
    m = m(3:s+2);
    

    译码中间用到dis 函数,测量两段序列的海明距离

    dis

    %% function to compute distances between x,y in the form of [0,0]  [1,1]
    function d = dis(x, y)   
      d = sum(xor(x,y));
    
    

    加入噪声,绘制误比特率随snr变化的曲线
    show error bit

    
    errbit  = zeros(1,21);
    for j = -5:15 
    
    y = awgn(x, j, 'measured');
    for i = 1:2*N
        if y(i) >=0.5
            y(i)=1;
        else
            y(i)=0;
        end
    end
    
    
    % 译码
    m=viterbi_hard(y); 
    errbit(j+6) = sum(m~=code_in)/N ;
    
    end
    
    logerr = 10*log10(errbit);
    plot( -5:15, logerr );
    
    展开全文
  • BPSK维特比译码仿真,包括卷积编码和交织技术
  • 卷积码编码维特比译码源代码

    热门讨论 2010-01-14 22:52:19
    卷积码编码维特比译码源代码 包括三个编译码文件,213编译码,217编译码等
  • 卷积编码维特比译码-卷积编码维特比译码.rar 卷积编码维特比译码相关文献 1.bmp
  • 卷积码的编码维特比译码程序,使用C/C++编写,可在Ubuntu和Windows操作系统上运行,经过修改可在嵌入式系统中运行。适合新手学习卷积码编码维特比译码算法,也可供工程师参考。
  • 采用MATLAB自编函数对卷积码以及维特比译码进行仿真,且对其性能进行分析。由于卷积码有性能floor,编码增益随信噪比降低而体现不明显。  1.引言  卷积码的编码器是由一个有k位输入、n位输出,且具有m位移位...
  • 采用MATLAB自编函数对卷积码以及维特比译码进行仿真,且对其性能进行分析。由于卷积码有性能floor,编码增益随信噪比降低而体现不明显。  1.引言  卷积码的编码器是由一个有k位输入、n位输出,且具有m位移位...
  • (171,133)卷加码及其2/3、3/4码率删除码的编译码,对比不同码率在不同信噪比条件下的抗噪声性能
  • 看懂这个算法,首先要了解序列标注任务 QQ522414928 可以在线交流 大体做一个解释,首先需要4个矩阵,当然这些矩阵是取完np.log后的结果, 分别是:初始strat→第一个字符状态的概率矩阵,转移概率矩阵,发射概率...

    看懂这个算法,首先要了解序列标注任务     QQ522414928 可以在线交流

     

    大体做一个解释,首先需要4个矩阵,当然这些矩阵是取完np.log后的结果,

    分别是:初始strat→第一个字符状态的概率矩阵,转移概率矩阵,发射概率矩阵,最后一个字符状态→end结束的概率矩阵,

    这些概率矩阵可以是通过统计得到,或者是LSTM+crf这种训练迭代得到。 

    zero_log 指的是在统计中发射概率没有的情况下用这个很小的值来代替,lstm+crf中应该不会出现不存在的发射概率。

     

    然后看代码

    一个矩阵V:里面保存的是每个时间步上的每个状态对应的概率

    一个字典path:里面保存的是  {当前标签:他之前所经过的路径}     

     

    然后最佳路径的计算经过三个部分:初试概率矩阵到第一个字符状态那部分,序列中字符状态转移和发射那部分,最后一个字符状态到end那部分

    里边的发射分数和转移分数都使用加法计算是因为   发射矩阵和转移矩阵都经过了log取对数运算

     

    def start_calcute(self,sentence):
            '''
            通过viterbi算法计算结果
            :param sentence: "小明硕士毕业于中国科学院计算所"
            :return: "S...E"
            '''
            zero = -3.14e+100
            zero_log = np.log(-3.14e+100)
            init_state = self.prob_dict["PiVector_prob"]
            trans_prob = self.prob_dict["TransProbMatrix_prob"]
            emit_prob = self.prob_dict["EmitProbMartix_prob"]
            end_prob = self.prob_dict["EndProbMatrix_prob"]
    
            V = [{}] #其中的字典保存 每个时间步上的每个状态对应的概率
            path = {}
    
            #初始概率
            for y in self.state_list:
                V[0][y] = init_state[y] + emit_prob[y].get(sentence[0],zero_log)
                path[y] = [y]
    		
            #从第二次到最后一个时间步
            for t in range(1,len(sentence)):
                V.append({})
                newpath = {}
                for y in self.state_list: #遍历所有的当前状态
                        temp_state_prob_list = []
                        for y0 in self.state_list: #遍历所有的前一次状态
                            cur_prob = V[t-1][y0]+trans_prob[y0][y]+emit_prob[y].get(sentence[t],zero_log)
                            temp_state_prob_list.append([cur_prob,y0])
    					#取最大值,作为当前时间步的概率
                        prob,state =  sorted(temp_state_prob_list,key=lambda x:x[0],reverse=True)[0]
                        #保存当前时间步,当前状态的概率
                        V[t][y] = prob
                        #保存当前的状态到newpath中
                        newpath[y] = path[state] + [y]
    			#让path为新建的newpath
                path = newpath
    
            #输出的最后一个结果只会是S(表示单个字)或者E(表示结束符)
            (prob, state) = max([(V[len(sentence)][y]+end_prob[y], y) for y in ["S","E"]])
            return (prob, path[state])
    

      

    展开全文
  • 自己写的卷积码编译码程序,已经测试通过程序无bug,欢迎大家下载参考
  • CVencode.m % 函数用于 CV 编码(任意 CV 代码) 用于维特比硬判决解码的 VAharddecode1.m % 函数CVsimhard.m 基本 CV 编码/解码BERcurve_CV_hard.m 计算模拟 BER 曲线实用功能获取代码参数bin2deci deci2bin
  • 利用matlab的communication toolbox实现AWGN信道下采用QPSK调制和卷积码编码,然后接收端采用维特比译码并且采用硬判决的系统最终得到的误码率曲线,并且采用BERtool工具将其与理论值进行比较。
  • 卷积码译码之维特比译码算法(Viterbi decoding algorithm) 本文主要介绍了卷积码的一种译码方法——维特比译码(Viterbi decoding)。 关键词:卷积码译码 维特比译码算法 卷积码简介:点击打开链接 ==========...
  • 卷积码编码 维特比译码 分组交织 去交织 编码输出至txt
  • 使用深度学习进行中文自然语言处理之序列标注
  • 卷积码在无线通信里用的非常广泛,通常卷积码编码器开始工作前都要进行初始化,按编码器的初始状态不同可以分为两类: 1.1 末尾补零卷积码(Tail-bits): 通常卷积码编码器开始工作时都要进行初始化,编码开始前将...
  • (2,1,3)卷积码编译码实现,信道加性高斯白噪声,BPSK调制
  • 对《通信原理》第七版(3,1,3)卷积码,进行维特比译码,适合参考。
  • % 2011-3-29 从matlab demos 当中找来的关于卷积编码维特比译码的实例figure(1) %subplot(211) %EbNo = 4.5:.5:7; linEbNo = 10.^(EbNo(:).*0.1);M = 4; codeRate = 1/2; constlen = 7; k = log2(M); codegen = ...
  • 1.引言卷积码的编码器是由一个有k位输入、n位输出,且具有m位移位寄存器构成的有限状态的有记忆系统,通常称它为时序网络。编码器的整体约束长度为v,是所有k个移位寄存器的长度之和。具有这样的编码器的卷积码称作[n...
  • 卷积编码--维特比译码

    千次阅读 2020-02-21 19:56:22
    卷积编码 在一个(n,k,m)的卷积编码器中,监督码元组的位数为n-k,这n-k位监督码元与k位信息组链接成n位编码序列。 卷积编码的表达方法有很多种,如离散卷积法、生成矩阵法、码生成多项式法 如图(2,1,2)卷积编码器...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,654
精华内容 661
关键字:

维特比编码