卷积码编码和维特比译码的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增加而增加 |