畅谈无线通信系统物理层之CRC(三)实现方式之逐比特计算法、并行计算法

四、实现方式

实现方式主要有逐比特计算法、并行计算法和查表法,逐比特计算法有2种方法,第一种是按照标准除法流程进行计算,第二种是按照标准除法等效的移位寄存器进行计算;并行计算法是一次进行多位bit的CRC计算,通过多位bit串联的CRC计算公式得到整个发送序列的CRC;查表法把整个发送序列的CRC值存在一张表中,通过查询即可得到CRC,发送序列太大时需要巨大的存储空间,所以这种方法只适用于发送序列较短的情况,或者可以作为并行计算算法的一部分,在进行多位bit的CRC计算时使用查表法,查表法不再叙述。

a)逐比特计算法

按照标准除法流程进行计算时的算法如下:

(1)在待传输的k比特数据后面加入(n-k)个0,即x^(n-k)M(x)

(2)取x^(n-k)M(x)的前(n-k+1)个数作余式r(x)的初始值

(3)While (数据未处理完),次数为k次

 Begin

        If (r(x)首位是1)

            r(x)= r(x)XOR g(x)

     else

          r(x)= r(x)

       end

         r(x)寄存器左移一位,按顺序读入一个新的x^(n-k)M(x)数据于r(x)寄存器的0 bit的位置。

  End

(4)  r(x)寄存器就是我们所要求的余数。

MATLAB脚本程序如下:

function crc_out = CRC_direct(g,din,InitialState,FinalXOR)  %%% g:生成多项式,din:数据输入
lg = length(g);      %%%%% g(x)的长度,为n-k+1
ld = length(din);     %%%%%%M(x)的长度,为k
din_addzeros = [din;zeros(lg,1)];  %%%%%在待传输的k比特数据后面加入(n-k+1)
%%个0,多加一个是为了为后一步时,r(x)的长度保持为n-k+1
r = din_addzeros(1:lg);  %%%%%取x^(n-k)M(x)的前(n-k+1)个数作余式r(x)的初始值
r(1:lg-1) = mod(din_addzeros(1:lg-1) +InitialState.',2) ;%%% r(x)的初始值和输入初始值异或
for ii=1:ld                           %%%%次数为k次 
    if r(1) ==0                       %%%If (r(x)首位是0)
        r(1:lg-1) = r(2:lg);             %%% r(x)寄存器左移一位
        r(lg)=din_addzeros(ii+lg);       %%%%读入一个新的x^(n-k)M(x)数据
    else                             %%%If (r(x)首位是1)  
        r(1:lg-1) = mod(r(2:lg) + g(2:lg).',2); %%% r(x)= r(x)XOR g(x),首位为0,移除
        r(lg)=din_addzeros(ii+lg);         %%%%读入一个新的x^(n-k)M(x)数据
    end
end
r(1:lg-1) =mod(r(1:lg-1) + FinalXOR.',2);           %%%与“结果异或值”进行异或
crc_out = [din;r(1:lg-1)];             %%%组成信息序列和CRC值

按照标准除法等效的移位寄存器进行计算的算法结构如下图所示:

算法流程如下:

(1) 设置CRC寄存器,如果有初值,并给其赋值为“余数初始值”;

(2) 将M(x)的第一个bit 与CRC寄存器最高位进行异或,该结果与g(x)的最低位到次高位进行相乘(g(x)的阶数比CRC高一阶,所以长度与CRC相同),记为rd,CRC寄存器最低位添0,向高位移位,在和rd异或后成为新的CRC寄存器;

(3)      M(x)下一个bit进行(2)操作;

(4)      M(x)最后一个bit进行(2)操作后,CRC寄存器即为CRC值;

(5)      最终CRC寄存器的内容与“结果异或值”进行或非操作后即为最终的CRC值。

原理阅了Peterson和Brown写的《Cyclic Codes for Error Detection》,除法如下图:

可以把输入数据拆开进行等效计算,输入数据为[1 1 1 0 1 0],如下所示,

[1 1 1 0 1 0] =[1 0 0 0 0 0 ] + [0 1 0 0 0 0] + [0 0 1 0 0 0] + [0 0 0 0 0 0] +

[0 0 0 0 1 0]  + [0 0 0 0 0 0]

 第一个先求[1 0 0 0 0 0 ]的余式,初始余式为全0,场景回到上一个余式,即此时的余式为全0,记为rx0=[0 0],rx0和[1 0 0 0 0 0 ]的高2位[1 0]异或后即为下一个余式的高2位[1 0],场景回到当前,[1 0]添0为[1 0 0],再处以[1 0 1],此时商1,[1 0 0]异或[1 0 1],余式为[0 0 1],高位舍弃,余式为[ 0 1],即[1 0 0 0 0 0 ]的最终余式为[0 1];

第二个求[0 1 0 0 0 0 ]的余式,上一个余式为[0 1],场景回到上一个余式,记为rx1=[0 1],rx1和[0 1 0 0 0 0 ]的2位[1 0]异或后即为下一个余式的高2位[1 1],场景回到当前,[1 1]添0为[1 1 0],再处以[1 0 1],此时商1,[1 1 0]异或[1 0 1],余式为[0 1 1],高位舍弃,余式为[ 1 1],即[0 1 0 0 0 0 ]的余式为[11];

第三个求[0 0 1 0 0 0 ]的余式,上一个余式为[1 1],场景回到上一个余式,记为rx2=[1 1],rx2和[0 0 1 0 0 0 ]的2位[1 0]异或后即为下一个余式的高2位[0 1],场景回到当前,[0 1]添0为[0 1 0],再处以[1 0 1],此时商0,[0 1 0]异或[0 0 0],余式为[0 1 0],高位舍弃,余式为[1 0],即[0 0 1 0 0 0 ]的余式为[10];     

以此类推,所以当前的输入与上一个的余式最高位的异或决定了当前的商值,异或为1,商为1,异或为0,商为0。

https://blog.csdn.net/u011639609/article/details/51464773中有过解释,参考如下图,g(x)=x3+x2+1。

信息序列还是1001,初始状态时,D1~D3三个寄存器的值都为0,输入的第一个信息比特是1,与D3的异或是1,这里为什么输入序列与D3做异或呢?其实这里隐含了一个选择操作,D1~D3代表当前的余式,D3表示余式的最高位。

场景回到上一个余式,此时,D3为上一个余式的次高位,上一个余式计算时,次高位与当前的下一个输入的异或值,即为下一个余式的初始的最高位,若最高位为1,下一个商为1,若最高位为0,下一个商为0。

场景回到当前,所以如果当前的输入与余式最高位D3相同的话,则当前输入与余式的模2加为0,即当前输入更新使得余式最高位为0,则此时商上面上0,不用对生成多项式做模2加运算,余式只要右移一位完成余式的更新操作(一个比特与0异或等于自身,所以这种情况下,电路中异或不起作用);另一种情况是,余式最高位D3与当前输入序列异或为1,表示当前输入更新后余式包含最高位,需要对生成多项式求余,求余操作通过电路中的异或进行,完成D1~D3的更新。具体步骤如下:

 (1) 初始状态:输入ui             D1    D2    D3            输出uo

                                        0       0        0

        (2)               1                     1       0        1                         1

        (3)                0                     1       1        1                         0

        (4)                0                     1       1        0                         0

        (5)                1                     1       1        0                         1

        D1~D3剩下的011即为监督比特,剩下3拍,逐比特输出:

        (6)               0                     0       1        1                         0

        (7)               0                     0       0        1                         1

        (8)               0                     0       0        0                         1

MATLAB脚本程序如下:

function crc_out = CRC_direct_LFSR(g,din,InitialState,FinalXOR)
lg = length(g);  
ld = length(din);
r = InitialState.';   %%%初始化
for ii=1:ld 
    rd = mod(r(1) + din(ii),2); %%%当前的输入与上一个的余式最高位的异或
    r(1:lg-2) = mod(r(2:lg-1) + rd*g(2:lg-1).',2); %%%按g(x)的多项式更新
    r(lg-1)=rd;    %%%%最低位为rd
end
 r(1:lg-1) =mod(r(1:lg-1) + FinalXOR.',2);
crc_out = [din;r(1:lg-1)]; 

按照标准除法等效的移位寄存器进行计算的算法还有一种结构,如下图所示:

b)并行计算法

并行计算法是一次进行多位bit的CRC计算,通过多位bit串联的CRC计算公式得到整个发送序列的CRC,多位bit可以为4位、8位或者16位。

以16位CRC为例介绍,如果要计算16位CRC,如下2种方式:

  • 一次可以进行8位CRC计算,再通过8位bit串联的CRC计算公式得到整个发送序列的CRC;
  • 一次可以进行16位CRC计算,计算方法即为正常CRC的方法:将本组CRC的值和下一组16位数据值异或后,重新计算得到下一组的CRC值,顺序执行即可得到整个发送序列的CRC;

8位bit的CRC计算值可以查表得到,用逐比特计算法计算得到表值 ;

8位bit串联的CRC计算公式如下,参见《通信系统中的CRC算法的研究和工程实现_廖海红》:

MATLAB脚本程序如下:

function crc_out = CRC_Paraller16_8(g,din,InitialState,FinalXOR,direct)
lg = length(g);
ld = length(din);
din(1:lg-1) = mod(din(1:lg-1) +InitialState.',2) ;
M = 8;
Num = ld/M;
    din_1 = din(1:M);  %%%第一组8bit
      if direct == 1
        r_M(:,1) = CRC_direct_LFSR(g,din_1,zeros(1,lg-1),FinalXOR);%%%直接法
      else
        r_M(:,1) = CRC_table(din_1);      %%%查表法
      end
    r = r_M(end-lg+2:end,1);
for ii=2:Num    %%%第二组到最后一组8bit
     rL = [r(9:16);zeros(8,1)];  %%低8位左移
     din_ii= mod(din(ii*M-M+1:ii*M) +r(1:8),2) ; %%取高8位
      if direct == 1
        r_M(:,ii) = CRC_direct_LFSR(g,din_ii,zeros(1,lg-1),FinalXOR); %%%直接法
     else
        r_M(:,1) =  CRC_table(din_1) ;   %%%查表法
      end   
    r = mod(rL + r_M(end-lg+2:end,ii),2);   %%%相加
end
 r(1:lg-1) =mod(r(1:lg-1) + FinalXOR.',2);
crc_out = [din;r(1:lg-1)];

独立16位bit的CRC计算公式可以根据生成多项式进行推导或者查表得到。

根据生成多项式进行推导16位bit的CRC计算公式的结构如下图所示:

对应的MATLAB脚本程序(在Zhao Baozhu的基础上修改的)如下:

para =16;   %%%%并行位数
gen = [1 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 1];  %%%生成多项式,x^16+x^15+x^2+1
crc_value = [diag(ones(1,length(gen)-1)),zeros(length(gen)-1,para)];%%%初始值
%%%%%% crc_value含义:第1行为CRC新值0的表达式,其中前16bit为CRC旧值,从低到高依次为CRC旧值0,1,…15,为1表示需要异或,为0表示不需要异或,17-32bit为输入bit,分别为d0,d1,…d15,d15表示第1个输入,d15表示第16个输入,依次类推,第2行为CRC新值1的表达式,…,第16行为CRC新值15的表达式。所以前16*16矩阵为diag,后16*16为全0,表示初始值。
%%%%%%
%%%%%%
    for i=1:para   
        dataXORcrc_value_end = crc_value(length(gen)-1,:);  %%%先取CRC旧值最高位
        dataXORcrc_value_end(length(gen)+para-i) = 1; %%%%输入bit置为1,相当于异或
        for j=length(gen)-1:-1:2             %%%%%%必须逆序,先把高位置为新值
            if gen(j)==1
                crc_value(j,:) = crc_value(j-1,:)+dataXORcrc_value_end;
            else
                crc_value(j,:) = crc_value(j-1,:);
            end
        end
        crc_value(1,:) = dataXORcrc_value_end;
    end
crc_value = mod(crc_value,2);
for i = 1:length(gen)-1
    s=sprintf('crc_new(%d) =',i-1);
    for j = 1:length(gen)+para-1
        if crc_value(i,j) == 1
            if j <length(gen)
                str=sprintf(' crc_old(%d)',j-1);
            else
                str=sprintf(' data_new(%d)',j-length(gen));
            end
            s=strcat(s,' +',str);
        end
    end
    simplepoly{i,1}=s;
end

运行后得到x^16+x^15+x^2+1的CRC16bit并行计算公式如下所示:

其中,crc_new(0) = + crc_old(0) + crc_old(4) + crc_old(8) + crc_old(11) + crc_old(12) + data_new(0) + data_new(4) + data_new(8) + data_new(11) + data_new(12),CRC旧值和data新值的序号一模一样,所以CRC旧值和data新值可以先进行异或成为新的data_new,再通过公式算出CRC,公式只取data_new相关的部分,例如:

crc_new(0) = data_new(0) + data_new(4) + data_new(8) + data_new(11) + data_new(12)。

以16位bit为单位连续bit的CRC计算的MATLAB脚本程序如下:

function crc_out = CRC_Paraller16_16(g,din,InitialState,FinalXOR,direct)
lg = length(g);
ld = length(din);
din(1:lg-1) = mod(din(1:lg-1) +InitialState.',2) ;
M = 16;
Num = ld/M;
    din_1 = din(1:M);  %%%第一组16bit
    if direct == 1
    r_M(:,1) = CRC_direct_LFSR(g,din_1,zeros(1,lg-1),FinalXOR); %%%直接法,也可以用根据生成多项式进行推导的公式或者查表法
    end
    r = r_M(end-lg+2:end,1);
for ii=2:Num 
    din_ii= mod(din(ii*M-M+1:ii*M) +r,2) ;  %%%和上一组的余式进行相加
    if direct == 1
     r_M(:,ii) = CRC_direct_LFSR(g,din_ii,zeros(1,lg-1),FinalXOR); %%%直接法,也可以用根据生成多项式进行推导的公式或者查表法
    end
    r = r_M(end-lg+2:end,ii);
end
 r(1:lg-1) =mod(r(1:lg-1) + FinalXOR.',2);
crc_out = [din;r(1:lg-1)];

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章