简介这套74循环码MATLAB实现资源面向通信工程、计算机科学等专业学生及编码理论初学者提供带GUI的编译码演示平台用于快速理解循环码的差错检测与纠正机制。压缩包共3个文件包含2个MATLAB脚本.m和1个图形界面文件.fig整体仅18KB轻量易用编码、解码及界面展示功能划分清晰。已有536人浏览学习适合作为信息论与编码课程配套实践工具。借助生成多项式G(x)x^3x1脚本完整实现4位信息到7位码字的编码与接收纠错解码GUI支持手动输入信息比特或码字并即时显示结果便于对照原理观察循环移位特性。在此基础上可修改生成多项式与码长参数灵活适配更多循环码场景为课程设计或毕业设计提供可直接运行的参考示例读者可借此快速验证不同输入序列下的编解码结果。1. 从一份 74cyclic-code.zip 工程说起搜到74cyclic-code.zip这个名字的人多半是在做通信原理课设或数字通信相关项目里面装的是 (7,4) 循环码的 MATLAB 实现通常带一个 GUI 交互界面。它解决的核心问题是把 4 位信息比特用生成多项式编码成 7 位码字送进信道接收端靠伴随式查表纠正 1 位随机错误再把信息还原出来。这类工程恰好覆盖了 MATLAB 里最典型的三块技能GF(2) 多项式运算、查表译码逻辑、GUI 事件回调也是matlab 下载安装后找练手项目的常见去处。适合通信原理课程设计、需要把循环码从公式落到可操作程序的工程师以及想了解 matlab gui 从零搭建一套小工具的人。下面按编码、译码、GUI、参数扩展、验证发布这条链路完整走一遍。2. (7,4) 循环码编译码的 MATLAB 实现2.1 生成多项式先写进变量(7,4) 循环码是汉明码的一种循环码形态码长 n7信息位 k4最小汉明距离 d3纠错能力 t1。所有这些性质都由生成多项式 g(x)x^3x1 决定二进制向量表示是[1 0 1 1]。也有教材选 x^3x^21向量1101两者都是 GF(2) 上的本原多项式都能构成循环码只是伴随式到错误位置的映射表不同。提示MATLAB 的 Communications Toolbox 提供了gfdeconv、encode、decode等现成函数但课设题目通常要求自实现下面给出的是只用基础 MATLAB 就能跑的算法。在代码里我一般把生成多项式放在脚本开头作为参数方便后续替换g [1 0 1 1]; % g(x) x^3 x 1(7,4) 循环码生成多项式 n 7; % 码长 k 4; % 信息位长参数说明g向量从最高次到最低次排列g(1)永远是 1校验位数量 n-k3恰好等于g向量长度减 1判断一个 7 位码字是否合法的依据是它能否被g(x)整除这就是循环码线性约束的数学形式。2.2 系统码编码除以生成多项式取余数循环码编码常做成系统码信息位保留在前面后面拼接校验位。步骤是先把 4 位信息向左移 3 位等价于乘以 x^3再对g(x)做模 2 除法把余数放到尾部。以信息位[1 0 1 0]为例被除式是 1010000除以 1011余数是 011码字就是 1010011。模 2 除法里没有借位减法被异或取代实现时用循环扫一遍即可function [q, r] polydiv_gf2(a, g) % a: 被除式系数向量高位在前 % g: 生成多项式系数向量高位在前 % q: 商r: 余数 a a(find(a, 1):end); % 去掉被除式前导 0 g0 g(find(g, 1):end); % 去掉生成式前导 0 r [a zeros(1, length(g0)-1)]; % 扩展余数寄存器 q zeros(1, length(r)); % 商向量初始全 0 for i 1:length(r)-length(g0)1 if r(i) 1 q(i) 1; r(i:ilength(g0)-1) xor(r(i:ilength(g0)-1), g0); end end r r(end-length(g0)2:end); % 取最后 length(g)-1 位作为余数 q q(1:length(a)-length(g0)1); end逻辑说明r(i) 1说明当前位的剩余部分最高位是 1需要用生成多项式异或消去如果r(i)是 0商这一位记为 0 并继续右移。MATLAB 索引从 1 开始循环上限length(r)-length(g0)1保证最后一次异或不会越界。最后r(end-length(g0)2:end)取的是余数的低 3 位因为 (7,4) 码余数最高 2 次、正好 3 个系数。有了除法编码函数就是移位加取余function cw enc74(msg, g) % msg 是 1x4 的 0/1 行向量高位在前 shifted [msg zeros(1, 3)]; % x^3 * m(x)左移 3 位 [~, rem] polydiv_gf2(shifted, g); rem rem(end-2:end); % 固定取出 3 位校验位 cw [msg rem]; % 信息位在前校验位在后 end参数说明rem(end-2:end)比直接用length更稳因为polydiv_gf2对前导零做了裁剪如果换成 x^3x^21 同样适用这一行不用改。验证一下对 16 个 4 位信息遍历编码用除法函数检查每个码字都能被g整除说明编码过程没有写错这一步建议在命令行直接跑g [1 0 1 1]; for m 0:15 msg de2bi(m, 4, left-msb); cw enc74(msg, g); [~, r] polydiv_gf2(cw, g); assert(all(r 0), 编码结果不合法); end disp(16 个码字全部通过整除校验);这段验证用了de2bi它属于 Communications Toolbox如果环境里没有可以用bitget(m, 4:-1:1)替代。2.3 伴随式译码一张位置映射表完成定位接收端拿到 7 位序列 rx 后直接对g(x)取余数得到的余数就是伴随式 s(x)。无错误时伴随式为全 0发生 1 位错误时伴随式与非零错误图样一一对应所以只要预先把 7 种单比特错误的伴随式算出来就能根据 s 反推出错误位置翻转后取前 4 位恢复信息。7 位错误位置与伴随式值的对应关系如下表按 g1011 计算错误位置MATLAB 索引错误多项式伴随式二进制 s2s1s0伴随式数值1最左位x^6x^610152x^511173x^411064x^301135x^210046x01027最右位x^010011注意这张表依赖生成多项式换成 g1101 时必须重新生成不能直接套用。表里规律来自 x^3 ≡ x1 (mod g)手算每一项错误项对这个余数关系化简即可。译码函数可以这样写function msg dec74(rx, g) % rx: 1x7 接收向量0/1 [~, s] polydiv_gf2(rx, g); % 伴随式 余数 s [zeros(1, 3-length(s)) s]; % 左补零到 3 位 s_val s * [4 2 1]; % 转成 1~7 的数值 pos_table [7 6 4 5 1 3 2]; % 下标伴随式值, 值出错位MATLAB索引 if s_val ~ 0 err_idx pos_table(s_val); % 取表定位 rx(err_idx) 1 - rx(err_idx); % 异或 1 翻转该位 end msg rx(1:4); % 纠完直接截取信息位 end逻辑说明pos_table的下标就是上表的伴随式数值例如 s_val5 查表得 1表示错误落在最左位翻转 rx(1)。s_val0 时不查表直接跳过。s * [4 2 1]把多项式系数 x2、x、x0 还原成十进制等价于二进制转十进制不需要任何工具箱。两个边界要说明第一如果接收向量发生 2 位及以上错误伴随式也会落在某个非零值上查表翻转后会得到错误结果(7,4) 码只能保证 t1 的纠错这是编码理论本身的限制第二某些 2 位错误画出来的伴随式恰好在表外属于不可检测错误这套机制同样无能为力。到这里编码 → 加错 → 译码的计算环节就可以在命令行跑通了。把 enc74 和 dec74 放在同一个脚本里反复模拟 1 位随机翻转可以稳定看到 msg 原样恢复。3. 用 MATLAB GUI 把循环码演示做成交互面板3.1 界面方案选型App Designer 还是 GUIDE搜索这套题目的人会碰到很多用 GUIDE 写的 .fig 工程。GUIDE 从 R2019b 开始不再推荐官方逐步把功能迁移到 App Designer新写代码建议直接用 App Designer别再做新的 .fig。App Designer 生成的 .mlapp 本质是类定义文件界面元素是 properties 里的对象属性回调函数是 methods 里的成员方法比 GUIDE 的分散函数结构更好维护也方便统一管理状态变量。提示如果手头是 .fig 老工程R2021b 之后打开并另存时 GUIDE 会提示迁移但实际项目里我一般直接重写迁移过程对 align 和 callback 引用的处理经常比重写还费时间。App Designer 里新建 Blank App画布左侧是控件面板右侧是代码视图。这个循环码演示面板需要 4 个区域输入区4 位信息位的编辑框、编码显示区7 位码字、信道干扰区下拉框选择翻转位置、输出区伴随式和译码结果。控件清单如下控件类型名称作用编辑框InfoEdit输入 4 位 0/1如 1010按钮EncodeButton触发编码文本CodeText显示 7 位码字下拉框ErrCombo选择 0~7 哪个位置翻转按钮CorruptButton在码字上翻转所选位文本RxText显示接收向量文本SyndText显示伴随式 s2s1s0按钮DecodeButton触发译码文本MsgHatText显示译码恢复的信息位布局上用 uigridlayout 固定行列避免窗口拉伸时控件乱跑。下拉框的选项值处理与普通 figure 的 uicontrol 略有差异要留意 Items 和 ItemsData 的配合。3.2 控件初始化和状态存储进入 App Designer 的 code view在 properties 段保存当前码字和生成多项式这两个状态需要在编码、加错、译码三个回调之间共享properties (Access private) g [1 0 1 1]; % 生成多项式 cw zeros(1, 7); % 当前系统码码字 rx zeros(1, 7); % 当前接收向量 end在 startupFcn 里初始化下拉框选项选项的 Value 要和实际比特位置完全对应。这里有个容易踩的坑下拉框显示第 3 位时用户理解是从左往右第 3 位而 MATLAB 索引正好也是 3把选项 Value 直接设成索引就能省掉一次转换app.ErrCombo.Items {无错误, 第1位, 第2位, 第3位, ... 第4位, 第5位, 第6位, 第7位}; app.ErrCombo.ItemsData 0:7; % 0 表示不翻转ItemsData 是每个选项背后实际存储的值读取用app.ErrCombo.Value得到的就是 0~7 的数值不需要在回调里做字符串匹配。3.3 回调函数数据从控件流向算法编码按钮的回调把输入框字符串解析成 4 位向量调用前面写的 enc74再渲染到界面function EncodeButtonPushed(app, event) str strtrim(app.InfoEdit.Value); if length(str) ~ 4 || any(~ismember(str, 01)) uialert(app.UIFigure, 请输入4位二进制数例如 1010, 输入错误); return; end msg double(str) - double(0); % 字符转数值 app.cw enc74(msg, app.g); % 调用算法层 app.CodeText.Value sprintf(%d %d %d %d %d %d %d, app.cw); end逻辑说明double(str) - double(0)把 0 和 1 字符统一转成 0/1 数值这比 str2num 更可靠str2num 解析 1010 会得到一个标量而不是向量显示时用 sprintf 带空格便于人眼分组。信道干扰按钮的回调读取 ErrCombo 中用户选的位置完成翻转function CorruptButtonPushed(app, event) idx app.ErrCombo.Value; % 0~70 表示不翻转 if idx 0 app.rx app.cw; else app.rx app.cw; app.rx(idx) 1 - app.rx(idx); % 翻转所选项 end app.RxText.Value sprintf(%d %d %d %d %d %d %d, app.rx); end这里 idx 直接用作 MATLAB 索引因为 ItemsData 里放的是 1~7。注意每次都要从app.cw重新拷贝再翻转而不是在上一次的 rx 上继续翻否则用户多点两次按钮就把两个位都翻转了超出了 (7,4) 码的纠错范围界面表现会让人误以为是 bug。译码回调调用 dec74 并展示中间量function DecodeButtonPushed(app, event) msg_hat dec74(app.rx, app.g); app.MsgHatText.Value sprintf(%d%d%d%d, msg_hat); [~, s] polydiv_gf2(app.rx, app.g); s [zeros(1, 3-length(s)) s]; app.SyndText.Value sprintf(%d%d%d, s); endSyndText 显示伴随式的意义在于把译码过程暴露出来伴随式为 000 表示无错非零时对应 2.3 节的查表关系。这一步直接把教材里的查表译码还原到界面上正是这类演示工具最常见的用法。到这里一个最小闭环已经成立编码 → 人为翻转 1 位 → 显示伴随式 → 译码恢复。如果还要体现循环码的循环性质可以在界面加一个循环左移按钮回调里每点一次把cw循环移位一位并显示移位后仍然能被g整除。这是循环码独有的特征也是标题里循环两个字最容易实操验证的点。4. 循环码参数边界与工程化扩展4.1 生成多项式换了查表就要跟着换(7,4) 循环码的另一个常用生成多项式是 x^3x^21向量 1101它与 x^3x1 互为对偶关系生成的码集合不同伴随式查找表也不同。网上很多代码把表写死成常数数组换多项式后译码直接错位这是最常见的坑之一。如果要把 GUI 里的生成多项式做成可下拉选择我的做法是运行时动态生成查表而不是维护两份手算表。用穷举生成function pos_table build_table(g) n 7; k 4; e eye(n); % 7 个单位错误向量 pos_table zeros(1, 2^(n-k)-1); % 7 个非零伴随式 for i 1:n [~, s] polydiv_gf2(e(i,:), g); s [zeros(1, 3-length(s)) s]; val s * [4 2 1]; pos_table(val) i; % 伴随式值 - 错误位置 end end参数说明e(i,:)是第 i 位为 1 的错误图样对每个错误图样求伴随式把结果 val 作为下标把位置 i 写入表。如果某个 val 冲突或表里有 0 残留说明 g 不是本原多项式(7,4) 结构不成立。这个函数把换多项式就要改表的处理自动化了在 GUI 里切换生成多项式时只需重新调用它。4.2 不可检测错误与误用边界(7,4) 码最小距离 3只能保证纠正任意 1 位错误。若信道中发生 2 位错误接收向量除以 g 的伴随式可能恰好等于某个单比特错误的伴随式译码器会误判成 1 位错误并纠正成另一个合法码字结果是错上加错。这不是代码 bug是编码增益有限。表现到 GUI 里很直观信道误码率高于一定阈值时界面显示纠错后的信息位仍然不对。我做演示时会在界面加一个备注区提示本程序针对 BSC 信道单比特错误设计2 位及以上错误不在保证范围。如果想提高纠错能力要把 (7,4) 换成 (15,11) 汉明码或 BCH 码生成多项式选用 x^4x1编码结构和查表函数完全复用只要改 n、k、g 三个参数。4.3 从 7/4 扩展到 15/11 的长码形态(15,11) 汉明码同样能纠 1 位错误冗余 4 位编码效率从 4/7 提升到 11/15。把 enc74/dec74 里的常数抽象成参数 n、k就能得到通用版本function cw enc_general(msg, g, n, k) r n - k; % 校验位个数 shifted [msg zeros(1, r)]; [~, rem] polydiv_gf2(shifted, g); rem [zeros(1, r-length(rem)) rem]; % 校验位左补零到 r 位 cw [msg rem]; end注意这里的rem [zeros(1, r-length(rem)) rem]因为余数在实际除法结果里可能不足 r 位比如余数只有 2 位时需要在前面补一个 0否则拼接出来的码字不是标准系统码。polydiv_gf2本身对前导零做了裁剪所以补零这一行不能省。(15,11) 的伴随式表会膨胀到 2^4-115 项build_table 按伴随式数值做下标天然支持表长变化不需要手工改数组。5. 循环码 GUI 的穷举验证与发布技巧5.1 全部 112 种场景的自动回归GUI 回调写完后不要只靠鼠标点几个 case最可靠的是写一个脱离界面的穷举脚本把16 个信息位 × 7 个翻转位置 112 种组合全部跑一遍% verify74.m g [1 0 1 1]; fail 0; for m 0:15 msg de2bi(m, 4, left-msb); cw enc74(msg, g); for e 1:7 % 最多翻转 1 位 rx cw; rx(e) 1 - rx(e); msg_hat dec74(rx, g); if ~isequal(msg, msg_hat) fail fail 1; fprintf(失败: msg%d%d%d%d e%d\n, msg, e); end end end fprintf(完成失败 %d 组\n, fail);这个脚本放在 GUI 工程目录的 test 子文件夹里每次改动 polydiv_gf2 或查表逻辑后跑一遍比直接在 App Designer 里点按钮高效得多。如果失败集中在某个特定错误位置优先怀疑查表如果全部失败检查 polydiv_gf2 的余数截断。没有 Communications Toolbox 的环境里把 de2bi 换成bitget(m, 4:-1:1)即可。5.2 打包成独立程序给其他人跑课设验收或把演示交给没有 MATLAB 的同学时常见做法是导出独立可执行文件。App Designer 的 APP 选项卡里有 Package 按钮底层调用 MATLAB Compiler 的compiler.build.standaloneApplication生成的文件需要在目标机器上安装对应版本的 MATLAB Runtime体积较大但不需要完整 MATLAB。如果对方本身有 MATLAB 环境导出.mlappinstall安装包更方便双击即可安装到 App 列表。提示打包前清掉 app 里的路径依赖脚本里不要用cd切目录运行时的工作目录和开发机不同任何依赖相对路径的文件都会在对方机器上报错。这类问题在实际打包里出现的概率远高于算法本身出错。另一个容易让 GUI 卡顿的细节是在回调里做长循环时要主动调用drawnow或者把耗时计算放到parfor。对 (7,4) 这种规模完全不需要但如果你把循环码扩展成更大码长单次编码虽然是 O(n)GUI 里频繁点按钮重算余数仍然会有肉眼可见的延迟。到了几十毫秒量级就要考虑把计算结果缓存下来比如把生成多项式 g 和查表结果在启动时算一次放进 properties回调里只做取数。我把 build_table 放在 startupFcn 里执行就是这个原因。如果还想给界面增加教学感可以在 SyndText 旁边加一个文本框把 polydiv_gf2 的中间异或步骤用字符串打印出来。这样伴随式不再是黑盒结果而是可以逐位看到除法过程对理解循环码原理、debug 查表、以及应对答辩提问都有实际帮助。本文还有配套的精品资源点击获取