实验六-线性分组码的MATLAB实现

信息论编码 专栏收录该内容
6 篇文章 6 订阅

信息论编码实验3~9连载,更多看专栏。

一、线性分组码原理介绍

1.1 信道编码基本概念

信道编码 也叫 差错控制编码,指在将要传输的信息序列中人为的添加一些保护成分(监督码元),从而在接收端译码时可以进行自动纠错(当然,这种纠错能力是有限的),从而增强了信号的抗干扰能力。

假设 发送序列长度n,其中信息码元数为 k监督码元数为 n-k,则:
编码效率(码率)= k / n;
冗余度= (n-k) / k;

信道编码分为 分组编码卷积编码。两者都是将原始信息流按长度 k 分组,再按照各自的编码规则映射成 长度为n的码组 进行发送。不同的是,对于每个码组的监督码元(长度为n-k),分组编码仅与本码组的 k 个信息码元有关,而卷积编码则同时与前 N-1 个码组及本码组的(共N*k个)信息码元有关(其中,N 被称为 编码约束度 )。

于是,根据上述定义的变量,对于上述两类编码有命名:
(n,k)分组码
(n,k,N)卷积码

本实验以(7,4)分组码为例演示分组编码,卷积编码则在实验七中进行演示。

1.2 分组编码

1.2.1 编码

M=(mk-1,mk-2,…,m1,m0)为单个码组中的信息码元;
C=(cn-1,cn-2,…,c1,c0)为分组编码后的分组码;
分组编码的映射规则如下:
在这里插入图片描述fi(·),(i=0,1,…,n-1)均为线性函数,则称 C线性分组码。若我们处理的码流都是二元信号(即元素为{0,1}),则称 C二元线性分组码。这也是本实验的情况。

也就是说,编码过程为 k 个信息码的线性组合:
在这里插入图片描述其中,G 是生成矩阵,由 k 个线性无关的行向量(基底)组成,注意到每一个基底都包含 n 个元素,它的系统形式(一种标准形式)为:
在这里插入图片描述则编码过程进一步写为:
在这里插入图片描述之所以再写一遍是因为终于出现了最开始提到的概念:
M 是信息码元;
MP 是监督码元;

于是,我们就完成了将 k 维空间的信息码元映射到n维空间的编码过程。这多出来的 n-k 维元素将帮助我们在接收端进行纠错。
在这里插入图片描述

1.2.2 译码

在二进制序列传输的过程的,显然不可避免的会出现差错,定义为:
错误图样 E=(en-1,en-2,…,e1,e0),发生错误的地方为1,其余为0;
则接收序列为 R = C + E 。根据神奇的线性代数知识,由于生成矩阵 G 是由 k 个线性无关的行向量组成,所以必然存在一个校验矩阵 H,使得 GHT=0,其中:在这里插入图片描述则解码过程为:
在这里插入图片描述其中, EHT 被称为 伴随式
由于 H 是已知的,所以根据接收结果我们就可以知道错误图样是什么,这就完成了纠错!!(感谢神奇的线性代数)。

1.2.3 线性分组码的距离和纠错能力

但是当然了,这个纠错能力是有限的,下面我们来具体的量化一下。

码距 定义为两个码组间对应位置不同的位数,也叫 汉明距离,记为 d。而所有码组间距离的最小值被称为 最小码距
在这里插入图片描述于是对于线性分组码,纠错能力 为:
在这里插入图片描述并且要注意:纠错能力 t 只是说明距离 t 以内的差错一定能纠正,并非说距离大于 t 的差错一定不能纠正。 因为纠错的本质就是计算当前的序列和哪个正确序列的码距最短,假如有一个码组错误位数超过 t,但是只有一个正确序列和它距离最短,那就可以认为纠正回来了。

另外,按照定义计算最小码距很复杂。但是再次根据神奇的线性代数知识(二元线性分组码的封闭性),任意两个线性码组之和仍为许用码组,所以那两个码组间的距离就是另一个码组的 码重(码组中1的个数)。所以 码组中的最小码重=最小码距

二、(7,4)汉明码编译码实例

终于可以看实例了。
假如我们选择(7,4)汉明码,且令生成矩阵为:
在这里插入图片描述

将待编码序列分成长度为 4 的信息码元,根据生成矩阵计算出监督码元
在这里插入图片描述
上面就是编码的过程,下面进行解码。

首先根据 1.2.2 的内容,由生成矩阵可以得到校验矩阵
在这里插入图片描述
再根据上面发送码组的所有情况,发现最小码重=3(0要排除在外),于是纠错能力t=1,于是根据只发生一位错误的错误图样得到伴随式(接收到码组的最后三位)的所有情况:
在这里插入图片描述
因为只有当伴随式=[000]的时候才认为没有错误,所以当伴随式为其他情况时,就默认错误是对应的错误图样,然后进行相应的纠正。当然,也有可能错了好几位也得到相同的伴随式,但那种情况我们(7,4)汉明码把握不住,所以也就按照上述那么处理了。这个时候就指望着我们的信道环境不要太差,错个一位就差不多得了。

然后再取出改正后码组的前四位,就得到我们的信息码元了。完成译码。

三、代码展示及运行结果

3.1 (7,4)汉明码编码

下面来验证上述编码实例,假设发送序列是M=[1 0 1 1 0 1 0 1 1 0],分组时不足 4 的整倍数将进行补零。

%% 实验六-汉明码编码过程
clear all
clc
%% 主函数
M = [1 0 1 1 0 1 0 1 1 0];
G = [1 0 0 0 1 1 0;...
     0 1 0 0 1 0 1;...
     0 0 1 0 0 1 1;...
     0 0 0 1 1 1 1];
C = hamming(M,G)

function C = hamming(M,G)
[k,n] = size(G);

% 输入序列补位
N = size(M,2);  % 获得输入序列元素个数
r = k-rem(N,k);   % 获得需要对输入序列进行补位的个数
M_add0 = [M,zeros(1,r)];% 补位

% 将输入信息序列进行分组
groups = ceil(length(M_add0)/k);    % 获得分组个数
M_dis = reshape(M_add0,[k,groups]).';
%{
M_dis = zeros(groups,k);
for i=1:groups
    M_dis(i,:) = M_add0(1,(1:k)*groups);
end
%}
% 生成编码结果C
C = mod(M_dis*G,2);% 生成结果别忘了对2取余

end

得到结果为:
在这里插入图片描述
每一行代表一个码组。

3.2 (7,4)汉明码译码

在上述编码的基础上添加译码子函数,完成对上述结果的译码。

%% 实验六-汉明码译码过程
clear all
clc
%% 主函数
% 生成分组编码C
M = [1 0 1 1 0 1 0 1 1 0];
G = [1 0 0 0 1 1 0;...
     0 1 0 0 1 0 1;...
     0 0 1 0 0 1 1;...
     0 0 0 1 1 1 1];
C = hamming(M,G);

% 生成错误图样
% 第一行未发生错误、第二行发生一位、第三行发生两位错误
e = [0 0 0 0 0 0 0;...
     1 0 0 1 0 0 0;...
     0 1 0 0 0 0 0];
R = mod(C+e,2);

C_result = decode(R,G)


% 汉明译码
function C_result = decode(R,G)
%{
输入:
    接收序列R
    生成矩阵G
输出:
    译码结果C_result
%}
% groups代表接收序列的编码组数
[groups,~] = size(R);
% k代表每组中的信息码元大小,n代表一个组里面包含样本点数
[k,n] = size(G);
% 根据G生成校验矩阵
H = [G(:,k+1:n).',eye(groups)];
% 生成伴随式S
S = mod(R*(H.'),2);
[S_row,S_column] = size(S);

% 设置伴随式和错误图样的对应元胞矩阵
SE = {[0 0 0],[0 0 0 0 0 0 0];...
      [0 0 1],[0 0 0 0 0 0 1];...
      [0 1 0],[0 0 0 0 0 1 0];...
      [1 0 0],[0 0 0 0 1 0 0];...
      [1 1 1],[0 0 0 1 0 0 0];...
      [0 1 1],[0 0 1 0 0 0 0];...
      [1 0 1],[0 1 0 0 0 0 0];...
      [1 1 0],[1 0 0 0 0 0 0]};

 % 找出计算出的伴随式所对应的错误图样,并进行纠正
C_result = zeros(S_row,n);
 [SE_row,SE_column] = size(SE); 
 for m=1:S_row
     for n=1:SE_row
         if all(S(m,:) == cell2mat(SE(n,1)))
             C_result(m,:) = R(m,:)+cell2mat(SE(n,2));
             C_result(m,:) = mod(C_result(m,:),2);
         end
     end
 end

C_result = C_result(:,1:k);

end

% 生成汉明码
function C = hamming(M,G)
[k,n] = size(G);

% 输入序列补位
N = size(M,2);  % 获得输入序列元素个数
r = mod(-rem(N,k),k);   % 获得需要对输入序列进行补位的个数
M_add0 = [M,zeros(1,r)];% 补位

% 将输入信息序列进行分组
groups = ceil(length(M_add0)/k);    % 获得分组个数
M_dis = reshape(M_add0,[k,groups]).';
%{
M_dis = zeros(groups,k);
for i=1:groups
    M_dis(i,:) = M_add0(1,(1:k)*groups);
end
%}
% 生成编码结果C
C = mod(M_dis*G,2);% 生成结果别忘了对2取余

end

译码结果为:
在这里插入图片描述
每一行对应一个码组,结合输入序列为[1 0 1 1 0 1 0 1 1 0],可以看出第三组最后两位进行了补零,而且结果很正确;但是第二组由于发生了两个错误所以没有正确的解译出来。

3.3 (7,4)汉明码性能探究

下面来点有意思的。在 AWGN 信道传输与 BPSK 调制的条件下,绘制未编码系统 与(7,4)汉明编码系统的误码率曲线。信噪比范围:0~5dB,系统流程图如下:
在这里插入图片描述碰到这种情况当然是进行蒙特卡洛仿真了,所以程序应该可以输入总共发送的符号数。并且,也要注意在发送过程中要是将矩阵形式转换成码流。另外还增加了子函数用于仿真BPSK-AWGN信道。代码如下:

%% 实验六-AWGN信道BPSK调制)未编码系统与(7,4)汉明码的BER-SNR对比
clear all
clc
%% 主函数
N = input('每个信噪比条件下要发送多少个样本点:');
snrdB_min = 0; snrdB_max = 5;% SNR范围是0-5dB
snrdB = snrdB_min:1:snrdB_max;
sym_initial = round(rand(1,N));% 生成原始序列

% 获得汉明码编码C_hamming
G = [1 0 0 0 1 1 0;...
     0 1 0 0 1 0 1;...
     0 0 1 0 0 1 1;...
     0 0 0 1 1 1 1];    % 生成矩阵G
[k,n] = size(G);
C_ham = hamming(sym_initial,G);
[groups,~] = size(C_ham);
C_ham_stream = reshape(C_ham.',[1,length(C_ham(:))]);% 转换成码流

% 两种序列都通过BPSK-AWGN信道
[~,BER_dir] = BPSK(sym_initial,snrdB);% 未编码码流
[RX_ham,~] = BPSK(C_ham_stream,snrdB);% 汉明编码码流

% 汉明码解码并计算SNR
errors_ham = zeros(1,length(snrdB));  % 预分配错误内存
for i=1:length(snrdB)
    RX_ham1 = reshape(RX_ham(i,:),[n,groups]).';
    C_result1 = decode(RX_ham1,G);
    errors_ham(i) = sum(sum(mod(C_result1+C_ham(:,1:k),2)));
end
BER_ham = errors_ham/N; % 得到BER

% 计算两种方案的BER-SNR曲线
figure
semilogy(snrdB,BER_dir,'*-',snrdB,BER_ham,'o-');grid on;
%axis([snrdB_min snrdB_max 0.0001 1]);
xlabel('信噪比 SNR / dB');ylabel('误码率 BER');
title(['发送的信息序列长度为 ',num2str(N)]);
legend('未编码系统','汉明编码系统');
% 直接显示数值
disp(['未编码系统',num2str(BER_dir)]);
disp(['汉明编码系统',num2str(BER_ham)]);

% 子函数-完成BPSK-AWGN信道的仿真
function [RX,BER] = BPSK(TX,SNR)
%{
输入:
    原始码元序列Tx
    信噪比范围SNR(dB)
输出:
    接收判别后序列RX
    信噪比对应的误码率序列BER
%}
N = length(TX);             % 获得原始序列长度
snr = 10.^(SNR/10);         % 转化成公制
len_snr = length(snr);      % 获得SNR范围长度

RX = zeros(len_snr,N);      % 预分配接收判别序列内存
errors = zeros(1,len_snr);  % 预分配错误内存
for j=1:len_snr             % 遍历所有SNR
   sigma = sqrt(1/(2*snr(j)));  % 计算SNRAWGN的标准差
   error_count = 0;
   for i=1:N                        % 遍历每一个发送符号
      x_d = 2*TX(i) - 1;            % 得到+1-1的发送序列
      n_d = sigma*randn(1);		    % GWN
      y_d = x_d + n_d;				% 加性噪声
      if y_d > 0
         RX(j,i) = 1;
      else
         RX(j,i) = 0;
      end
      if (RX(j,i) ~= TX(i))
         error_count = error_count + 1;	% 对错误样本进行计数
      end
   end
   errors(j) = error_count;	% 得到该信噪比下的错误个数
end
BER = errors/N;	% BER estimate
end


% 子函数-汉明码编码
function C = hamming(M,G)
[k,n] = size(G);

% 输入序列补位
N = size(M,2);  % 获得输入序列元素个数
r = mod(-rem(N,k),k);   % 获得需要对输入序列进行补位的个数
M_add0 = [M,zeros(1,r)];% 补位

% 将输入信息序列进行分组
groups = ceil(length(M_add0)/k);    % 获得分组个数
M_dis = reshape(M_add0,[k,groups]).';

% 生成编码结果C
C = mod(M_dis*G,2);% 生成结果别忘了对2取余

end


% 子函数-汉明码解码
function C_result = decode(R,G)
%{
输入:
    接收序列R
    生成矩阵G
输出:
    译码结果C_result
%}
[k,n] = size(G);
% 根据G生成校验矩阵
H = [G(:,k+1:n).',[1 0 0;0 1 0;0 0 1]];
% 生成伴随式S
S = mod(R*(H.'),2);
[S_row,S_column] = size(S);

% 设置伴随式和错误图样的对应元胞矩阵
SE = {[0 0 0],[0 0 0 0 0 0 0];...
      [0 0 1],[0 0 0 0 0 0 1];...
      [0 1 0],[0 0 0 0 0 1 0];...
      [1 0 0],[0 0 0 0 1 0 0];...
      [1 1 1],[0 0 0 1 0 0 0];...
      [0 1 1],[0 0 1 0 0 0 0];...
      [1 0 1],[0 1 0 0 0 0 0];...
      [1 1 0],[1 0 0 0 0 0 0]};

 % 找出计算出的伴随式所对应的错误图样,并进行纠正
C_result = zeros(S_row,n);
 [SE_row,SE_column] = size(SE); 
 for m=1:S_row
     for n=1:SE_row
         if all(S(m,:) == cell2mat(SE(n,1)))
             C_result(m,:) = R(m,:)+cell2mat(SE(n,2));
             C_result(m,:) = mod(C_result(m,:),2);
         end
     end
 end

C_result = C_result(:,1:k);

end

我输入就假设发送的原始码流有50w个符号,输出结果如下:
在这里插入图片描述
在这里插入图片描述
可以看出,增加分组码可以明显的提升系统性能,针不戳。

四、程序自评价

1.关于切割码流。编码函数只需要输入码流和生成矩阵,这很好。但是编码函数将各码组按行输出,想要传送还需要将其在主函数中 reshape 成码流;解码函数也需要提前将码流 reshape 成切割好的码流,这造成程序的移植性差。需要的小伙伴可以将 reshape 的过程放到子函数里,我就懒得改了。
2.自动计算(n,k)。这是优点,汉明码的编解码函数可以自动识别(n,k),用户只要输入自己的生成函数和码流即可(包括上面切割的过程也不需要调参),这增强了移植性。

代码原创,但因为原理编写参考到了实验课的指导书,假如有什么不对的地方,侵删。

  • 6
    点赞
  • 1
    评论
  • 48
    收藏
  • 打赏
    打赏
  • 扫一扫,分享海报

©️2022 CSDN 皮肤主题:1024 设计师:我叫白小胖 返回首页

打赏作者

虎慕

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值