如题,代码终于跑动啦,记录一下,写了很多注释,可以自行观看。

%%卷积码(n,k,N)=(2,1,7) 约束长度为7
%生成多项式(171,133)
% = G[1 1 1 1 0 0 1
%     1 0 1 1 0 1 1]
clc;clear all;close all;

data = randi([0,1],1,100);
% data = [1 0 1 1 1 0 1 0 0 0 0 0 0 0 0 0 ];
G_matrix = [1 1 1 1 0 0 1;1 0 1 1 0 1 1]; 
trellis = poly2trellis(7,[171,133]);  %生成网格图
% code_data = convenc(data,trellis); 
code_data = conv_encode(data,G_matrix);  %卷积编码 比convenc长N

%% bpsk
data_bio = 1 - 2*code_data;  %1- -1 0- 1

fs = 2e6;
rs = 1e6;
rolloff = 0.35;
EbN0 = 20;
SNR = EbN0 +10 * log10(2) - 10 * log10(fs/rs);
data_noise = awgn(data_bio,SNR,'measured');



data_decision = (sign(data_noise)-1 )/-2;  %判决

%%
hui_len = 35; %回溯长度
% data_out = vitdec(data_decision,trellis,hui_len,'trunc','hard'); %解码 截断到数据发送完,硬判决
data_out = conv_decode(data_decision,G_matrix);
error_bits = sum(data_out(hui_len+1:end-7)~=data(hui_len+1:end)); % 计算错误比特
% 计算误码率
BER = error_bits/length(data); % 误码率
disp(BER);

%% 编码函数
%% 本函数实现卷积编码 适用于所有的[n,1,N]卷积编码
%{
输入:
    原始序列m
    生成矢量G
输出:
    卷积码结果C
特点:
    适用于所有的[n,1,N]卷积编码
%}
function C = conv_encode(m,G)

len = length(m);
k=1;    % 表示每次对k个码元进行编码
[n,N] = size(G);% n表示一个输入码元拥有几个输出,N表示每次监督的输入码元数
C = zeros(1,n*len);  %%一共输出的个数
% 在头尾补0,方便卷积输出和寄存器清洗
m_add0 = [zeros(1,N-1),m,zeros(1,N+1)];

% 循环每一位输入符号,获得输出矩阵
C_reg = fliplr(m_add0(1,1:N));  %如果是1:N,c_reg开头是m第一个数
    for i =1:len+N
        %生成每一位输入符号的n位输出
        C(n*i-(n-1):n*i) =  mod(C_reg*G.',2);
        
        %更新寄存器序列+待输出符号(共N个符号)
        C_reg = [m_add0(i+N),C_reg];% 添加新符号
        C_reg(end) = [];% 挤掉旧符号
    end

end
%% 译码函数
%% 本函数实现卷积码译码
function V = conv_decode(m,G)
%{
输入:
    接收到的卷积码 m
    生成矢量G  每一行代表一个输出的连接结构 与发送机编码器结构相同
输出:
    卷积码译码结果C 
特点:
    适用于所有的[n,1,N]卷积译码
%}
len = length(m);    %记录输入信息长度
K=1;                %每次输入一个码元
%通过 n和len的值来判断总共需要几个解码循环
[n,N] =  size(G);   %n表示发送端一次有几个码元输出 N表示一次有几个监督码元(包括当前输入)
times = len/n;      %解码循环总次数 即原信息的长度
%states = ZW_B_Comb(N-1);    %共有N-1种状态 states存储了每一种状态

%状态寄存器 由于有N-1个编码器(不包含输入) 所以共有种状态 最多时候每种状态有两种路径 所以共有2^N种情况
%每种状态又有累计路径值、累计输入、以及当前状态三种情况 
C_regs = cell(2^(N),4);  %每一列代表一个状态 同一列中每一行分别表示当前状态的不同情况
C_regs_reg = cell(2^(N-1),3);
%初始时从网格图中第一个状态开始出发 为0 
C_regs{1,1} = 0;       %第一行代表此时这个状态累计路径值 初始为零
C_regs{1,2} = [];      %第二行存储输入的信息 一开始没有输入
C_regs{1,3} = zeros(1,N-1);   %第三行存储该寄存器此时存储的状态是什么 共2^(N-1)可能
C_regs{1,4} = 1;       %第四行代表是否被征用 1代表这个寄存器被征用了 0代表空闲
for i=2:1:2^(N)     %将初始累计值均设置为0 且初始时除1外全为空闲
    C_regs{i,1}=0;
    C_regs{i,4}=0;
end
for i=1:times
    code_buffer=m(1,(i-1)*n+1:i*n); %第一次输出时对应的码元 共n个
    a_b=find([C_regs{:,4}]==0); %返回为0的序号 看哪个没有被使用
    n_b=2^N-length(a_b);          %说明有n_b个征用的状态等待去进入下两个状态
    %第i个元素储存了编码器对应状态的前两条输入对应的寄存器序号  此状态二进制换成十进制正好等于i-1 
    states = cell(1,2^(N-1));  
    for j=1:n_b
        in_0 = j;       %第j个与此时是序号最靠前的第一个为空闲的状态C_regs的 将是上一个第j个输入分别为0、1后
        in_1 = a_b(j);  %对应的状态
        put_0 = mod( [0 C_regs{j,3}] *G.',2); %求出输入为0时,此状态对应的输出
        put_1 = mod( [1 C_regs{j,3}] *G.',2); %求出输入为1时,此状态对应的输出
        
        C_regs1_in_0 = C_regs{j,1} + sum(xor(put_0,code_buffer)); %计算输入为0时的路径累计值(汉明距离)
        C_regs1_in_1 = C_regs{j,1} + sum(xor(put_1,code_buffer)); %计算输入为0时的路径累计值
        C_regs2_in_0 = [C_regs{j,2} 0];    %得到这条路径的累积输入信息码
        C_regs2_in_1 = [C_regs{j,2} 1];    %得到这条路径的累积输入信息码
        C_regs_in_0 = [0 C_regs{j,3}];    %计算输入为0时的下一个状态的情况 
        C_regs_in_0(end) = [];               %删除多余单元
        C_regs{in_0,4} = 1;                  %代表被征用 
        states{1,B_T(C_regs_in_0)+1} = [states{1,B_T(C_regs_in_0)+1} in_0];%储存相同状态的两条路径
       
        C_regs_in_1 = [1 C_regs{j,3}];    %计算输入为1时的下一个状态的情况
        C_regs_in_1(end) = [];               %删除多余单元
        C_regs{in_1,4} = 1;                  %代表被征用  
        states{1,B_T(C_regs_in_1)+1} = [states{1,B_T(C_regs_in_1)+1} in_1];%储存相同状态的两条路径
        % 计算完毕后再一并赋值 不能就算一个就赋值一个 因为in_1也要用到in_0原来的值
        C_regs{in_0,1} = C_regs1_in_0;
        C_regs{in_1,1} = C_regs1_in_1;
        C_regs{in_0,2} = C_regs2_in_0;
        C_regs{in_1,2} = C_regs2_in_1;
        C_regs{in_0,3} = C_regs_in_0;
        C_regs{in_1,3} = C_regs_in_1;
    end
    if n_b == 2^(N-1)   %说明现在每个状态都有两个输入 该将每个状态的最小累计路径取出来并删除最大累计路径了
        for k=1:2^(N-1)
            %Len = length(states{1,k});    %计算每个状态对应来源路径的累计路径数
            if C_regs{states{1,k}(1),1} > C_regs{states{1,k}(2),1} %选出最近的路径所在的寄存器序号
                i_tmw = states{1,k}(2);
            else
                i_tmw = states{1,k}(1);
            end
            %weight_list=[C_regs{states{1,k},1}];   %找出当前状态对应来源路径的累计路径值
            %[~,i_tmw]=sort(weight_list); %从小到大排列 并返回此时序列对应排列前的序号
            C_regs_reg{k,1}=C_regs{i_tmw,1};%只找出每个状态最短的累计路径 
            C_regs_reg{k,2}=C_regs{i_tmw,2};%并重新存储在序号前2^(N-1)的状态寄存器中
            C_regs_reg{k,3}=C_regs{i_tmw,3};
        end
        for k=1:2^(N-1)    %将挑选出来的2^(N-1)个状态重新从小到大赋给原寄存器
            C_regs{k,1}=C_regs_reg{k,1};%只找出每个状态最短的累计路径 
            C_regs{k,2}=C_regs_reg{k,2};%并重新存储在序号前2^(N-1)的状态寄存器中
            C_regs{k,3}=C_regs_reg{k,3};
        end
        for L=2^(N-1)+1:2^N %将后2^(N-1)个寄存器释放
            C_regs{L,4}=0;  
        end
    end
end
n_survival_branch=length(find([C_regs{:,4}]==1)); %找出被征用(存储了状态的寄存器序号) 一般为前2^(N-1)
w_survival_branch=[C_regs{1:n_survival_branch,1}];%找出被征用(存储了状态的寄存器序号)的累计路径值 
[~,i_weight]=sort(w_survival_branch);             %把各状态的累计路径值从小到大排列 并返回对应从前所在序列的序号值
i_minimum_weight=i_weight(1);                     %i_weight(1)最后所有状态中累计路径值最小的状态序号
V=C_regs{i_minimum_weight,2};                     %输出最有可能的输入情况
end
%% 
%% 本递归函数实现由二进制得出十进制
function C = B_T(Q)  
    len = length(Q);
    C=0;
    for i=1:len
        C = Q(i) * 2^(i-1)+C;
    end
end