卷积码编码和维特比译码的MATLAB仿真程序

卷积码编码和维特比译码的MATLAB仿真程序。卷积码是通信系统中广泛使用的前向纠错编码,而维特比译码是实现其最大似然译码的高效算法。

卷积码与维特比译码:完整实现

核心原理概述

  • 卷积码编码:通过移位寄存器和模2加法器实现,将k个输入比特映射为n个输出比特(码率R=k/n)
  • 维特比译码:基于网格图的最大似然序列估计算法,通过动态规划寻找最可能发送的序列

下图展示了完整的编码-传输-译码流程:
译码路径
原始信息比特
卷积码编码器
BPSK调制
AWGN信道
接收信号
硬判决路径
硬判决维特比译码
软判决路径
软判决维特比译码
硬判决译码结果
软判决译码结果
性能对比分析

完整MATLAB实现程序

1. 主程序:完整仿真流程
matlab 复制代码
%% 卷积码编码与维特比译码完整仿真程序
clear; close all; clc;
fprintf('========== 卷积码与维特比译码仿真 ==========\n');

%% 1. 参数设置
% 卷积码参数 (2,1,7) 经典码
R = 1/2;                    % 码率: 1/2
K = 7;                      % 约束长度
G = [133 171];              % 生成多项式 (八进制)
trellis = poly2trellis(K, G);  % 生成网格结构

% 仿真参数
numBits = 1000;             % 信息比特数
EbN0_dB = 0:1:6;            % 信噪比范围 (dB)
numTrials = 100;            % 每个信噪比下的仿真次数

% BPSK调制参数
bpskModulator = @(x) 2*x - 1;   % 0-> -1, 1-> +1
bpskDemodulator = @(x) x > 0;   % 正数->1, 负数->0

%% 2. 预分配结果存储
numSNR = length(EbN0_dB);
ber_hard = zeros(1, numSNR);    % 硬判决误比特率
ber_soft = zeros(1, numSNR);    % 软判决误比特率
ber_uncoded = zeros(1, numSNR); % 未编码误比特率

%% 3. 主仿真循环
fprintf('开始仿真...\n');

for snrIdx = 1:numSNR
    fprintf('处理 SNR = %.1f dB... ', EbN0_dB(snrIdx));
    
    % 临时计数器
    errorCount_hard = 0;
    errorCount_soft = 0;
    errorCount_uncoded = 0;
    totalBits = 0;
    
    % 多次试验取平均
    for trial = 1:numTrials
        %% 3.1 生成随机信息比特
        infoBits = randi([0 1], 1, numBits);
        
        %% 3.2 卷积码编码
        codedBits = convenc(infoBits, trellis);
        
        %% 3.3 BPSK调制
        % 编码信号
        modulatedCoded = bpskModulator(codedBits);
        
        % 未编码信号(用于比较)
        modulatedUncoded = bpskModulator(infoBits);
        
        %% 3.4 通过AWGN信道
        % 计算噪声功率
        EbN0 = 10^(EbN0_dB(snrIdx)/10);
        EsN0_coded = EbN0 * R;      % 编码符号的信噪比
        EsN0_uncoded = EbN0;        % 未编码符号的信噪比
        
        % 计算噪声标准差
        noiseVar_coded = 1/(2*EsN0_coded);
        noiseVar_uncoded = 1/(2*EsN0_uncoded);
        
        % 添加高斯噪声
        receivedCoded = modulatedCoded + sqrt(noiseVar_coded) * randn(size(modulatedCoded));
        receivedUncoded = modulatedUncoded + sqrt(noiseVar_uncoded) * randn(size(modulatedUncoded));
        
        %% 3.5 译码
        % 硬判决译码
        hardDecision = bpskDemodulator(receivedCoded);
        decoded_hard = vitdec(hardDecision, trellis, 5*K, 'trunc', 'hard');
        
        % 软判决译码
        decoded_soft = vitdec(receivedCoded, trellis, 5*K, 'trunc', 'unquant');
        
        % 未编码信号解调
        decoded_uncoded = bpskDemodulator(receivedUncoded);
        
        %% 3.6 误比特率统计
        errorCount_hard = errorCount_hard + sum(infoBits ~= decoded_hard);
        errorCount_soft = errorCount_soft + sum(infoBits ~= decoded_soft);
        errorCount_uncoded = errorCount_uncoded + sum(infoBits ~= decoded_uncoded);
        totalBits = totalBits + numBits;
    end
    
    % 计算平均误比特率
    ber_hard(snrIdx) = errorCount_hard / totalBits;
    ber_soft(snrIdx) = errorCount_soft / totalBits;
    ber_uncoded(snrIdx) = errorCount_uncoded / totalBits;
    
    fprintf('硬判决BER: %.2e, 软判决BER: %.2e\n', ber_hard(snrIdx), ber_soft(snrIdx));
end

%% 4. 理论性能曲线
% 未编码BPSK理论误比特率
theory_uncoded = 0.5 * erfc(sqrt(10.^(EbN0_dB/10)));

% 硬判决译码的理论近似(2,1,7)卷积码
dfree = 10;  % 自由距离
Ad = [0 0 0 0 0 36 0 211 0 1404 0 11633 0 77433 0 502690 0 3322763 0 21292910]; % 重量枚举
theory_hard = zeros(size(EbN0_dB));
for i = 1:length(EbN0_dB)
    Pb = 0;
    for d = dfree:2:(dfree+20)
        if d <= length(Ad) && Ad(d) > 0
            Pb = Pb + Ad(d) * qfunc(sqrt(d * R * 10^(EbN0_dB(i)/10)));
        end
    end
    theory_hard(i) = Pb;
end

%% 5. 结果可视化
figure('Position', [100, 100, 1200, 800]);

% 5.1 误比特率曲线
subplot(2, 3, [1, 2]);
semilogy(EbN0_dB, ber_uncoded, 'b-o', 'LineWidth', 2, 'MarkerSize', 8, 'DisplayName', '未编码(仿真)');
hold on;
semilogy(EbN0_dB, theory_uncoded, 'b--', 'LineWidth', 1.5, 'DisplayName', '未编码(理论)');
semilogy(EbN0_dB, ber_hard, 'r-s', 'LineWidth', 2, 'MarkerSize', 8, 'DisplayName', '硬判决译码(仿真)');
semilogy(EbN0_dB, theory_hard, 'r--', 'LineWidth', 1.5, 'DisplayName', '硬判决译码(理论)');
semilogy(EbN0_dB, ber_soft, 'g-d', 'LineWidth', 2, 'MarkerSize', 8, 'DisplayName', '软判决译码(仿真)');
xlabel('E_b/N_0 (dB)'); ylabel('误比特率 (BER)');
title('卷积码性能比较: (2,1,7)码');
legend('Location', 'southwest'); grid on;
ylim([1e-6, 1]);

% 5.2 编码增益分析
subplot(2, 3, 3);
% 在BER=1e-4处的编码增益
targetBER = 1e-4;
[~, idx_uncoded] = min(abs(ber_uncoded - targetBER));
[~, idx_hard] = min(abs(ber_hard - targetBER));
[~, idx_soft] = min(abs(ber_soft - targetBER));

codingGain_hard = EbN0_dB(idx_uncoded) - EbN0_dB(idx_hard);
codingGain_soft = EbN0_dB(idx_uncoded) - EbN0_dB(idx_soft);

barData = [codingGain_hard, codingGain_soft];
bar(1:2, barData, 'FaceColor', [0.3, 0.6, 0.9]);
set(gca, 'XTickLabel', {'硬判决', '软判决'});
ylabel('编码增益 (dB) @ BER=10^{-4}');
title('编码增益分析');
grid on;

% 添加数值标签
for i = 1:2
    text(i, barData(i)+0.1, sprintf('%.2f dB', barData(i)), ...
        'HorizontalAlignment', 'center', 'FontWeight', 'bold');
end

%% 6. 详细分析单个信噪比下的性能
fprintf('\n========== 详细分析 (Eb/N0 = 4dB) ==========\n');
testSNR = 4;  % dB
testIdx = find(EbN0_dB == testSNR, 1);

if ~isempty(testIdx)
    % 运行一次详细的仿真
    infoBits = randi([0 1], 1, numBits);
    codedBits = convenc(infoBits, trellis);
    modulated = bpskModulator(codedBits);
    
    % 添加噪声
    EbN0 = 10^(testSNR/10);
    noiseVar = 1/(2*EbN0*R);
    received = modulated + sqrt(noiseVar) * randn(size(modulated));
    
    % 不同译码方式
    hardDecision = bpskDemodulator(received);
    decoded_hard = vitdec(hardDecision, trellis, 5*K, 'trunc', 'hard');
    decoded_soft = vitdec(received, trellis, 5*K, 'trunc', 'unquant');
    
    % 计算误比特位置
    errorPos_hard = find(infoBits ~= decoded_hard);
    errorPos_soft = find(infoBits ~= decoded_soft);
    
    % 5.3 错误分布
    subplot(2, 3, 4);
    stem(1:numBits, infoBits, 'b', 'Marker', 'none', 'LineWidth', 0.5);
    hold on;
    stem(errorPos_hard, ones(size(errorPos_hard)), 'r', 'filled', 'MarkerSize', 4);
    stem(errorPos_soft, 0.8*ones(size(errorPos_soft)), 'g', 'filled', 'MarkerSize', 4);
    xlabel('比特位置'); ylabel('比特值/错误标记');
    title(sprintf('错误分布 (Eb/N0 = %d dB)', testSNR));
    legend('信息比特', '硬判决错误', '软判决错误', 'Location', 'best');
    xlim([1, min(100, numBits)]);  % 只显示前100个比特
    grid on;
    
    % 5.4 接收信号分布
    subplot(2, 3, 5);
    % 发送+1对应的接收信号
    received_plus1 = received(codedBits == 1);
    received_minus1 = received(codedBits == 0);
    
    histogram(received_plus1, 50, 'FaceColor', 'r', 'FaceAlpha', 0.6, 'EdgeColor', 'none');
    hold on;
    histogram(received_minus1, 50, 'FaceColor', 'b', 'FaceAlpha', 0.6, 'EdgeColor', 'none');
    xlabel('接收信号幅度'); ylabel('频数');
    title('接收信号分布');
    legend('发送+1 (比特1)', '发送-1 (比特0)', 'Location', 'best');
    grid on;
    
    % 5.5 误比特率随回溯深度变化
    subplot(2, 3, 6);
    tracebackDepths = K:2:10*K;
    ber_vs_depth = zeros(size(tracebackDepths));
    
    for dIdx = 1:length(tracebackDepths)
        depth = tracebackDepths(dIdx);
        decoded = vitdec(received, trellis, depth, 'trunc', 'unquant');
        ber_vs_depth(dIdx) = sum(infoBits ~= decoded) / numBits;
    end
    
    plot(tracebackDepths, ber_vs_depth, 'b-o', 'LineWidth', 2, 'MarkerSize', 6);
    xlabel('回溯深度'); ylabel('误比特率');
    title('译码性能 vs 回溯深度');
    grid on;
    
    % 标记最优回溯深度(5K是经验值)
    hold on;
    plot([5*K, 5*K], [0, max(ber_vs_depth)], 'r--', 'LineWidth', 1.5);
    text(5*K, max(ber_vs_depth)/2, sprintf('5K=%d', 5*K), ...
        'HorizontalAlignment', 'right', 'FontWeight', 'bold');
    
    % 显示统计信息
    fprintf('硬判决译码错误数: %d/%d (BER = %.2e)\n', ...
        length(errorPos_hard), numBits, length(errorPos_hard)/numBits);
    fprintf('软判决译码错误数: %d/%d (BER = %.2e)\n', ...
        length(errorPos_soft), numBits, length(errorPos_soft)/numBits);
    fprintf('软判决相对硬判决的增益: %.2f dB\n', ...
        10*log10(length(errorPos_hard)/length(errorPos_soft)));
end

%% 7. 自定义维特比译码器详细实现
fprintf('\n========== 自定义维特比译码器实现 ==========\n');

% 为了教学目的,实现一个简化的维特比译码器
[infoBits_test, decodedBits_custom, trellis_info] = custom_viterbi_demo(numBits/10, testSNR, trellis);

% 计算自定义译码器的性能
ber_custom = sum(infoBits_test ~= decodedBits_custom) / length(infoBits_test);
fprintf('自定义维特比译码器BER: %.2e\n', ber_custom);

%% 8. 不同卷积码性能比较
fprintf('\n========== 不同卷积码性能比较 ==========\n');

% 定义几种经典卷积码
convolutional_codes = {
    struct('name', '(2,1,3)', 'K', 3, 'G', [5 7]),       % 约束长度3
    struct('name', '(2,1,5)', 'K', 5, 'G', [23 35]),     % 约束长度5
    struct('name', '(2,1,7)', 'K', 7, 'G', [133 171]),   % 约束长度7
    struct('name', '(2,1,9)', 'K', 9, 'G', [561 753]),   % 约束长度9
};

figure('Position', [100, 100, 1000, 600]);
colors = lines(length(convolutional_codes));

for codeIdx = 1:length(convolutional_codes)
    code = convolutional_codes{codeIdx};
    trellis_i = poly2trellis(code.K, code.G);
    
    % 快速仿真该码的性能
    ber_code = zeros(size(EbN0_dB));
    
    for snrIdx = 1:length(EbN0_dB)
        errorCount = 0;
        totalCount = 0;
        
        for trial = 1:10  % 减少仿真次数以加快速度
            infoBits_i = randi([0 1], 1, numBits/10);
            codedBits_i = convenc(infoBits_i, trellis_i);
            modulated_i = bpskModulator(codedBits_i);
            
            EbN0_i = 10^(EbN0_dB(snrIdx)/10);
            noiseVar_i = 1/(2*EbN0_i*R);
            received_i = modulated_i + sqrt(noiseVar_i) * randn(size(modulated_i));
            
            decoded_i = vitdec(received_i, trellis_i, 5*code.K, 'trunc', 'unquant');
            
            errorCount = errorCount + sum(infoBits_i ~= decoded_i);
            totalCount = totalCount + length(infoBits_i);
        end
        
        ber_code(snrIdx) = errorCount / totalCount;
    end
    
    % 绘制性能曲线
    semilogy(EbN0_dB, ber_code, 'o-', 'Color', colors(codeIdx,:), ...
        'LineWidth', 2, 'MarkerSize', 6, 'DisplayName', code.name);
    hold on;
end

xlabel('E_b/N_0 (dB)'); ylabel('误比特率 (BER)');
title('不同约束长度卷积码性能比较 (软判决)');
legend('Location', 'southwest'); grid on;
ylim([1e-5, 1]);

fprintf('仿真完成!\n');
2. 自定义维特比译码器实现(教学目的)
matlab 复制代码
function [infoBits, decodedBits, trellis_info] = custom_viterbi_demo(numBits, EbN0_dB, trellis)
% 自定义维特比译码器实现(简化版,用于教学演示)
    
    fprintf('运行自定义维特比译码器演示...\n');
    
    % 参数
    R = 1/2;  % 码率
    numStates = 2^(trellis.numInputSymbols*(trellis.constraintLength-1));
    
    % 生成测试数据
    infoBits = randi([0 1], 1, numBits);
    
    % 编码
    codedBits = convenc(infoBits, trellis);
    
    % BPSK调制
    modulated = 2*codedBits - 1;
    
    % 添加噪声
    EbN0 = 10^(EbN0_dB/10);
    noiseVar = 1/(2*EbN0*R);
    received = modulated + sqrt(noiseVar) * randn(size(modulated));
    
    % 维特比译码参数
    tracebackDepth = 5 * log2(numStates);
    
    %% 步骤1: 构建网格图信息
    fprintf('  构建网格图...\n');
    [nextStates, outputs] = build_trellis_info(trellis);
    
    %% 步骤2: 初始化路径度量和路径历史
    numTimeSteps = numBits + tracebackDepth;  % 包括尾比特
    pathMetrics = inf * ones(numStates, numTimeSteps);
    pathMetrics(1, 1) = 0;  % 从状态0开始
    
    survivorPaths = zeros(numStates, numTimeSteps);
    
    %% 步骤3: 前向计算(Viterbi算法核心)
    fprintf('  执行维特比算法...\n');
    
    for t = 1:numTimeSteps-1
        % 获取当前时间步的接收符号(2个比特对应一个符号)
        if 2*t <= length(received)
            receivedSymbol = received(2*t-1:2*t);
        else
            % 尾比特处理
            receivedSymbol = [0; 0];
        end
        
        % 对每个状态进行更新
        for currentState = 1:numStates
            currentStateIdx = currentState - 1;  % 转换为0-based索引
            
            % 如果当前状态路径度量为无穷大,跳过
            if pathMetrics(currentState, t) == inf
                continue;
            end
            
            % 获取可能的下一状态和输出
            for inputBit = 0:1
                nextState = nextStates(currentStateIdx+1, inputBit+1);
                outputBits = outputs(currentStateIdx+1, inputBit+1);
                
                % 将输出转换为BPSK符号
                expectedSymbol = 2*de2bi(outputBits, 2, 'left-msb')' - 1;
                
                % 计算分支度量(欧氏距离的平方)
                branchMetric = sum((receivedSymbol - expectedSymbol).^2);
                
                % 计算候选路径度量
                candidateMetric = pathMetrics(currentState, t) + branchMetric;
                
                % 更新下一状态的路径度量
                nextStateIdx = nextState + 1;  % 转换为1-based索引
                if candidateMetric < pathMetrics(nextStateIdx, t+1)
                    pathMetrics(nextStateIdx, t+1) = candidateMetric;
                    survivorPaths(nextStateIdx, t+1) = currentState;
                end
            end
        end
        
        % 显示进度
        if mod(t, 10) == 0
            fprintf('    处理时间步 %d/%d\n', t, numTimeSteps-1);
        end
    end
    
    %% 步骤4: 回溯找到最佳路径
    fprintf('  回溯寻找最佳路径...\n');
    
    % 找到最终状态(应该是状态0)
    [~, finalState] = min(pathMetrics(:, end));
    
    % 回溯重建信息序列
    decodedBits = zeros(1, numBits);
    currentState = finalState;
    
    for t = numTimeSteps:-1:2
        prevState = survivorPaths(currentState, t);
        
        % 确定输入比特
        if t <= numBits+1  % 只记录信息比特部分
            % 查找从prevState到currentState的输入比特
            prevStateIdx = prevState - 1;
            for inputBit = 0:1
                if nextStates(prevStateIdx+1, inputBit+1) == currentState-1
                    if t-1 <= numBits
                        decodedBits(t-1) = inputBit;
                    end
                    break;
                end
            end
        end
        
        currentState = prevState;
    end
    
    %% 步骤5: 输出网格图信息
    trellis_info.nextStates = nextStates;
    trellis_info.outputs = outputs;
    trellis_info.numStates = numStates;
    trellis_info.pathMetrics = pathMetrics;
    
    fprintf('  自定义维特比译码完成。\n');
end

function [nextStates, outputs] = build_trellis_info(trellis)
% 构建网格图的下一状态和输出表
    
    numStates = trellis.numStates;
    numInputs = trellis.numInputSymbols;
    
    nextStates = zeros(numStates, numInputs);
    outputs = zeros(numStates, numInputs);
    
    for currentState = 0:numStates-1
        for inputBits = 0:numInputs-1
            % 计算下一状态
            nextState = trellis.nextStates(currentState+1, inputBits+1);
            
            % 计算输出
            outputBits = trellis.outputs(currentState+1, inputBits+1);
            
            % 存储
            nextStates(currentState+1, inputBits+1) = nextState;
            outputs(currentState+1, inputBits+1) = outputBits;
        end
    end
end
3. 可视化网格图和状态转移
matlab 复制代码
%% 网格图和状态转移可视化
function visualize_trellis(trellis, maxTime)
% 可视化卷积码的网格图
    
    fprintf('可视化网格图...\n');
    
    numStates = trellis.numStates;
    
    % 创建图形
    figure('Position', [100, 100, 1200, 600]);
    
    % 绘制状态节点
    for t = 0:maxTime
        for s = 0:numStates-1
            % 计算坐标
            x = t;
            y = s;
            
            % 绘制节点
            plot(x, y, 'bo', 'MarkerSize', 10, 'MarkerFaceColor', 'b');
            hold on;
            
            % 标注状态号
            text(x, y+0.1, sprintf('%d', s), ...
                'HorizontalAlignment', 'center', 'FontSize', 8);
        end
    end
    
    % 绘制状态转移(分支)
    [nextStates, outputs] = build_trellis_info(trellis);
    
    for t = 0:maxTime-1
        for currentState = 0:numStates-1
            for inputBit = 0:1
                nextState = nextStates(currentState+1, inputBit+1);
                outputBits = outputs(currentState+1, inputBit+1);
                
                % 计算坐标
                x1 = t;
                y1 = currentState;
                x2 = t+1;
                y2 = nextState;
                
                % 选择颜色:输入0为红色,输入1为蓝色
                if inputBit == 0
                    lineColor = [1, 0.2, 0.2];  % 红色
                else
                    lineColor = [0.2, 0.2, 1];  % 蓝色
                end
                
                % 绘制连线
                plot([x1, x2], [y1, y2], 'Color', lineColor, 'LineWidth', 1.5);
                
                % 标注输出比特(在连线中点)
                midX = (x1 + x2) / 2;
                midY = (y1 + y2) / 2;
                
                outputStr = dec2bin(outputBits, 2);
                text(midX, midY, outputStr, ...
                    'HorizontalAlignment', 'center', ...
                    'BackgroundColor', 'white', ...
                    'FontSize', 8, 'Margin', 0.5);
            end
        end
    end
    
    % 美化图形
    xlabel('时间步'); ylabel('状态');
    title(sprintf('卷积码(%d,%d,%d)网格图', ...
        trellis.numInputSymbols, trellis.numOutputSymbols, ...
        trellis.constraintLength-1));
    grid on;
    xlim([-0.5, maxTime+0.5]);
    ylim([-0.5, numStates-0.5]);
    set(gca, 'YTick', 0:numStates-1);
    
    % 添加图例
    hold on;
    plot([-1, -1], [-1, -1], 'r-', 'LineWidth', 2, 'DisplayName', '输入0');
    plot([-1, -1], [-1, -1], 'b-', 'LineWidth', 2, 'DisplayName', '输入1');
    legend('Location', 'northeast');
    
    fprintf('网格图可视化完成。\n');
end

% 调用可视化函数
visualize_trellis(trellis, 8);

参考代码 卷积码编码和维特比译码的有关功能 www.youwenfan.com/contentcsp/97419.html

关键参数说明

参数 说明 典型值
码率 ® 信息比特数与编码比特数的比例 1/2, 1/3, 2/3
约束长度 (K) 影响编码记忆深度 3, 5, 7, 9
生成多项式 (G) 八进制表示,定义编码器连接 5 7, 133 171
回溯深度 维特比译码的回溯长度 5×K (经验值)
自由距离 (dfree) 卷积码的最小汉明距离 随K增加而增加
相关推荐
名字还没想好☜2 分钟前
Go 的 select 实战:超时、非阻塞收发与优雅退出的三个套路
开发语言·数据库·golang·go·并发
geovindu3 分钟前
java: Backtracking Algorithm
java·开发语言·windows·后端·算法·回溯算法
Scott9999HH9 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
码智社10 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海10 小时前
python 列表、元组、集合和字典
开发语言·python
萧瑟余晖10 小时前
JDK 26 新特性详解
java·开发语言
马优晨11 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
人邮异步社区13 小时前
怎么把C语言学到精通?
c语言·开发语言
心平气和量大福大13 小时前
C#-WPF-控件-TextBox 数据绑定
开发语言·c#·wpf
ttwuai14 小时前
Cursor 生成 CRUD 后,Go 后台接口别只测 200:JWT、RBAC 和 tenant_id 怎么验
开发语言·后端·golang