信道编码是为了保证通信系统的传输可靠性,克服信道中的噪声和干扰而专门设计的一类抗干扰技术和方法。它根据一定的监督规律在待发送的信息码元中(人为的)加入一些必要的监督码元,在接收端利用这些监督码元与信息码元之间的监督规律,发现和纠正差错,以提高信息码元传输的可靠性。
其中,称待发送的码元为信息码元,人为加入的多余码元为监督(或校验)码元。信道编码的目的,是以最少的监督码元为代价,换取最大程度的可靠性提高。
常用的信道编码有线性分组码、循环码、BCH码、RS码和卷积码等。
在IEEE 802.11a中,主要使用了卷积编码,卷积码也是分组的,但它的监督码元不仅与本组的信息元有关,而且还与前若干组的信息元有关。这种码的纠错能力强,不仅可纠正随机差错,而且可纠正突发差错。卷积码根据需要,有不同的结构及相应的纠错能力,但其编码规律都是相同的。
下图以一个约束长度为4、码率 R=1/3 的卷积编码器为例。
IEEE 802.11a协议中规定卷积编码使用的生成多项式 g0 = 133(8进制)和 g1 = 171(8进制),码率为1/2。
注: Signal 域的卷积编码是1/2速率,而Data 域的卷积编码可以根据不同的速率需要进行删余,我们选用的是3/4速率。
摘录《GB 15629.1101-2006》,关于卷积编码器的部分:
也许这样说太空泛了,照例附上代码就清楚到底怎么卷积编码的了。
`timescale 1ns / 10ps
//////////////////////////////////////////////////////////////////////////////////
// Create Date: 15:37:29 10/08/2014
// Design Name: convolution
// Module Name: conv_encoder
// Project Name: OFDM base on Xilinx KC705
// Description: OFDM 卷积模块,符合IEEE 802.11a 标准,1/2码率卷积输出。
// 生成多项式为g0 = 133[8],g1 = 171[8],码率为1/2。[8]:8进制
// Revision: 1.0
// Copyright: 《基于xlinx FPGA的OFDM通信系统基带设计》
//////////////////////////////////////////////////////////////////////////////////
module conv_encoder (clk, aclr, data_in, nd, data_out_v, rdy);
input aclr; // 异步复位,低电平有效
input clk; // 60Mhz
input data_in; // 加扰模块输出的数据
input nd; // 输入有效信号
output [1:0] data_out_v;
output rdy;
reg [6:1] shift_reg;
reg [1:0] data_out_v;
reg rdy;
always @ ( negedge aclr or posedge clk )
begin
if ( ! aclr )
begin
shift_reg <= 6'b000000;
data_out_v <= 2'b00;
rdy <= 1'b0 ;
end
else
if ( nd )
begin
data_out_v[0] <= shift_reg[6] + shift_reg[5] + shift_reg[3] + shift_reg[2] + data_in; //数据A多项式:S(x) = x^6 + x^5 + x^3 + x^2 + 1
data_out_v[1] <= shift_reg[6] + shift_reg[3] + shift_reg[2] + shift_reg[1] + data_in; //数据B多项式:S(x) = x^6 + x^3 + x^2 + x + 1
rdy <= 1'b1;
shift_reg <= { shift_reg [5:1], data_in };
end
else
rdy <= 1'b0;
end
endmodule
1/2码率的卷积编码模块,重要的部分是生成表达式:
来说说删余部分,无线通信基带信号处理中,为了提高传输效率,在卷积编码后一般要进行删余(puncture)操作,即周期性的删除一些相对不重要的数据比特,引入了删余操作的卷积编码也称做删余卷积码。
在编码进行了删余操作后,需要在译码时进行depuncture,即在译码之前删余比特位置加以填充。
删余编码规则:
R = 3/4
R = 2/3
本工程采用的是signal域 1/2卷积编码,data域 3/4编码,所以整体为多速率的卷积编码,可以有速率选择。
模块结构框图:
模块有二个时钟,一个是输入时钟,一个是输出时钟(按照所得速率不同而不同)。
signal域,输入时钟(20M),输出时钟(40M)。
data域, 输入时钟(60M),输出时钟(80M)。
模块思想是:首先生成1/2码率的卷积码,然后缓存起来,根据不同的速率要求,给予不同的输出方式。
verilog代码:
`timescale 1ns / 10ps
//////////////////////////////////////////////////////////////////////////////////
// Create Date: 15:57:29 10/08/2014
// Design Name: convolution
// Module Name: data_conv_encoder
// Project Name: OFDM base on Xilinx KC705
// Description: OFDM 卷积模块,符合IEEE 802.11a 标准,多码率卷积输出。本工程输出是1/2和3/4码率。
// 3/4码率由1/2码率删余处理得到。
// Revision: 1.0
// Copyright: 《基于xlinx FPGA的OFDM通信系统基带设计》
//////////////////////////////////////////////////////////////////////////////////
module data_conv_encoder (DCONV_DIN, DCONV_ND, RATE_CON, DCONV_RST, DCONV_CLK_I,
DCONV_CLK_O, DCONV_DOUT, DCONV_INDEX, DCONV_RDY);
input DCONV_DIN; // 输入数据
input DCONV_ND; // 输入有效信号
input [3:0] RATE_CON; // 由Signal域得到的编码速率,决定截断方式
input DCONV_RST; // 复位信号,低电平有效
input DCONV_CLK_I; // 卷积码输入时钟(60Mhz)
input DCONV_CLK_O; // 卷积码输出时钟,根据截断方式不同,分别为输入时钟的3/4倍或2/3倍。(这里是80Mhz,数据的4/3倍)
output DCONV_DOUT; // 输出数据
output [8:0] DCONV_INDEX; // 输出数据计数
output DCONV_RDY; // 输出数据有效信号
wire [1:0] DATA_OUT_V;
wire RDY;
reg BUF_RDY;
reg [1:0] i;
reg [2:0] j;
reg [1:0] Puncture_BUF_12;
reg [5:0] Puncture_BUF_34;
reg [3:0] Puncture_BUF_23;
reg [9:0] INDEX_TEMP;
reg DCONV_DOUT;
reg [8:0] DCONV_INDEX;
reg DCONV_RDY;
conv_encoder conv_base(
.data_in(DCONV_DIN),
.nd(DCONV_ND),
.clk(DCONV_CLK_I),
.aclr(DCONV_RST), // 异步复位,低电平有效
.data_out_v(DATA_OUT_V),
.rdy(RDY)
);
always @ ( negedge DCONV_RST or posedge DCONV_CLK_I ) // 将产生的卷积数据存入buff中
begin
if(!DCONV_RST)
begin
Puncture_BUF_12 <= 2'd0;
Puncture_BUF_34 <= 6'd0;
Puncture_BUF_23 <= 4'd0;
i <= 2'd0;
end
else
begin
if(RDY)
case(RATE_CON)
4'b1101,4'b0101,4'b1001: // Rate is 1/2 .
begin
Puncture_BUF_12 <= DATA_OUT_V;
BUF_RDY <= 1;
end
4'b1111,4'b0111,4'b1011,4'b0011: // Rate is 3/4 .
begin
case(i)
2'b00:
begin
Puncture_BUF_34 [1:0] <= DATA_OUT_V;
BUF_RDY <= 1;
i <= i + 2'd1;
end
2'b01:
begin
Puncture_BUF_34 [3:2] <= DATA_OUT_V;
BUF_RDY <= 1;
i <= i + 2'd1;
end
2'b10:
begin
Puncture_BUF_34 [5:4] <= DATA_OUT_V;
BUF_RDY <= 1;
i <= 2'd0;
end
default:
begin
Puncture_BUF_34 <= 6'd0;
BUF_RDY <= 0;
i <= 2'd0;
end
endcase
end
4'b0001: // Rate is 2/3 .
begin
case(i)
2'b00:
begin
Puncture_BUF_23 [1:0] <= DATA_OUT_V;
BUF_RDY <= 1;
i <= i + 2'd1;
end
2'b01:
begin
Puncture_BUF_23 [3:2] <= DATA_OUT_V;
BUF_RDY <= 1;
i <= 2'd0 ;
end
default:
begin
Puncture_BUF_23 <= 4'd0;
BUF_RDY <= 0;
i <= 2'd0 ;
end
endcase
end
endcase
else
begin
BUF_RDY <= 0;
Puncture_BUF_12 <= 2'd0;
Puncture_BUF_34 <= 6'd0;
Puncture_BUF_23 <= 4'd0;
i <= 2'd0;
end
end
end
always @ ( negedge DCONV_RST or posedge DCONV_CLK_O ) // 删余,即从buff中取对应的数据输出
begin
if(!DCONV_RST)
begin
DCONV_DOUT <= 0 ;
DCONV_RDY <= 0;
j <= 3'b000;
end
else
begin
if(BUF_RDY)
case(RATE_CON)
4'b1101,4'b0101,4'b1001: // Rate is 1/2 .
begin
case(j)
3'b000:
begin
DCONV_DOUT <= Puncture_BUF_12 [j] ;
DCONV_RDY <= 1;
j <= j +1 ;
end
3'b001:
begin
DCONV_DOUT <= Puncture_BUF_12 [j] ;
DCONV_RDY <= 1;
j <= 3'b000 ;
end
default:
begin
DCONV_DOUT <= 0 ;
DCONV_RDY <= 0;
j <= 3'b000 ;
end
endcase
end
4'b1111,4'b0111,4'b1011,4'b0011: // Rate is 3/4 .
begin
case(j)
3'b000,3'b001,3'b010:
begin
DCONV_DOUT <= Puncture_BUF_34 [j] ;
DCONV_RDY <= 1;
j <= j + 1 ;
end
3'b011:
begin
DCONV_DOUT <= Puncture_BUF_34 [j+2] ;
DCONV_RDY <= 1;
j <= 3'b000 ;
end
default:
begin
DCONV_DOUT <= 0;
DCONV_RDY <= 0;
j <= 0;
end
endcase
end
4'b0001: // Rate is 2/3 .
begin
case(j)
3'b000,3'b001:
begin
DCONV_DOUT <= Puncture_BUF_23 [j] ;
DCONV_RDY <= 1;
j <= j + 1 ;
end
3'b010:
begin
DCONV_DOUT <= Puncture_BUF_23 [j] ;
DCONV_RDY <= 1;
j <= 3'b000 ;
end
default:
begin
DCONV_DOUT <= 0 ;
DCONV_RDY <= 0 ;
j <= 0 ;
end
endcase
end
endcase
else
begin
DCONV_DOUT <= 0 ;
DCONV_RDY <= 0 ;
end
end
end
always @ ( negedge DCONV_RST or posedge DCONV_CLK_O ) // 有效数据的个数
begin
if(!DCONV_RST)
begin
DCONV_INDEX <= 0 ;
INDEX_TEMP <= 0;
end
else
begin
if(BUF_RDY)
case(RATE_CON)
4'b1101,4'b1111:
begin
if(INDEX_TEMP < 47)
begin
INDEX_TEMP <= INDEX_TEMP + 1 ;
DCONV_INDEX <= INDEX_TEMP ;
end
else
begin
INDEX_TEMP <= 0 ;
DCONV_INDEX <= INDEX_TEMP ;
end
end
4'b0101,4'b0111:
begin
if(INDEX_TEMP < 95)
begin
INDEX_TEMP <= INDEX_TEMP + 1 ;
DCONV_INDEX <= INDEX_TEMP ;
end
else
begin
INDEX_TEMP <= 0 ;
DCONV_INDEX <= INDEX_TEMP ;
end
end
4'b1001,4'b1011:
begin
if(INDEX_TEMP < 191)
begin
INDEX_TEMP <= INDEX_TEMP + 1 ;
DCONV_INDEX <= INDEX_TEMP ;
end
else
begin
INDEX_TEMP <= 0 ;
DCONV_INDEX <= INDEX_TEMP ;
end
end
4'b0001,4'b0011:
begin
if(INDEX_TEMP < 287)
begin
INDEX_TEMP <= INDEX_TEMP + 1 ;
DCONV_INDEX <= INDEX_TEMP ;
end
else
begin
INDEX_TEMP <= 0 ;
DCONV_INDEX <= INDEX_TEMP ;
end
end
endcase
else
DCONV_INDEX <= 0 ;
end
end
endmodule