尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
MATLAB 2FSK调制解调仿真代码拆解:从信源到误码率曲线
简介这份资源是面向通信工程、电子信息类专业学生的课程设计参考资料围绕2FSK调制与解调系统的设计与仿真展开适合正在学习通信系统原理、需要完成MATLAB课程设计或想加深数字调制理解的学习者。压缩包内共1个doc文档约579KB内容涵盖设计任务与要求、方案论证、2FSK调制与解调原理、源程序代码及仿真结果分析完整呈现了从信源产生、键控法调制、加性高斯白噪声信道到相干解调与误码率性能评估的全过程。读者可从中获取可直接参考的MATLAB7.0源程序、系统模块参数设置思路、SNR与误码率曲线绘制方法以及相干解调与非相干解调的对比分析便于快速搭建仿真链路并完成波形分析与性能评价。目前已有853人学习下载对需要撰写课程设计报告或验证通信算法的人来说是一份结构清晰、实用性较强的参考材料。1. 从一份课设代码说起2FSK 调制解调到底怎么跑起来很多通信原理课设的代码拿过来一跑就报错或者波形出来跟书上完全对不上。我最近拆了一份基于 MATLAB 的 2FSK 调制与解调系统仿真代码它把信源、调制、高斯信道、相干解调、非相干解调、误码率统计串成了一条完整链路最后还画出了眼图和升余弦滤波前后的波形对比。这份代码适合正在做通信课设、想搞懂 2FSK 从比特流到误码率曲线全过程的开发者。它不依赖 Simulink 模块纯脚本实现参数集中、结构清晰改几个变量就能换码速率、换信噪比、换调制方式。下面我按“能干什么 → 怎么跑 → 怎么改”的顺序把这份代码拆开讲一遍。2. 系统架构与信号流从随机比特到误码率曲线2.1 模块划分与数据流向这份代码的架构很直白就是通信系统教材里那张经典框图信源 → 调制 → 信道 → 解调 → 信宿 → 性能分析。它没有用 Simulink 搭模块而是用 MATLAB 脚本函数把每一步串起来好处是参数全在脚本开头改起来不用翻模块属性。信源部分用randsrc产生 25 个二进制随机码码速率Fd1系统采样频率Fs40载频Fc10。调制用dmod函数指定fsk方式频率间隔df10。信道用awgn加高斯白噪声信噪比通过SNRpBit换算。解调分两路相干解调用ddemod的fsk/eye模式非相干用fsk/eye/noncoh。最后用symerr统计误码率用simbasebandex画误码率曲线。整个数据流是单向的没有反馈环所以调试起来比较直观——哪一步波形不对就单独把那个变量拿出来画图看。2.2 关键参数与物理含义参数集中在脚本开头我整理成表方便对照修改参数名值含义改动影响Fc10载波频率影响调制后波形疏密Fs40系统采样频率必须满足奈奎斯特太低会混叠Fd1码速率决定码元宽度N40每码元采样点数Fs/Fd眼图横轴长度df10频率间隔2FSK 两个载频之差numSymb25仿真码元数太少误码率统计不准SNRpBit60每比特信噪比控制噪声强度M2进制数2FSK 固定为 2这里有个容易翻车的地方Fs和Fc的关系。如果Fs小于2*Fc调制波形会混叠画出来完全不是正弦波。我一般会先检查Fs 4*Fc留够余量。2.3 调制与信道仿真链路调制部分用dmod一行搞定% 调制参数 Fc 10; % 载频 Fs 40; % 系统采样频率 Fd 1; % 码速率 df 10; % 频率间隔 M 2; % 进制数 % 产生二进制随机码 numSymb 25; x randsrc(numSymb, 1, [0:M-1]); % 2FSK 调制 y dmod(x, Fc, Fd, Fs, fsk, M, df);dmod的第二个参数是载频第三个是码速率第四个是采样频率第五个指定调制方式第六个是进制数第七个是频率间隔。这里df10意味着两个载频分别是Fc-df/2和Fcdf/2也就是 5 和 15。如果df太小两个频率靠得太近解调时带通滤波器分不开误码率会飙升。信道部分加高斯白噪声% 信噪比换算 SNRpBit 60; SNR SNRpBit / log2(M); % 加入高斯白噪声 seed [12345 54321]; randn(state, seed(2)); y awgn(y, SNR - 10*log10(0.5) - 10*log10(N), measured, [], dB);这里SNR - 10*log10(0.5) - 10*log10(N)是把每比特信噪比换算成awgn函数需要的采样点信噪比。N是每码元采样点数0.5是单极性信号的功率因子。这个换算公式如果写错噪声强度会差好几个数量级波形要么干净得不像话要么全是毛刺。2.4 相干与非相干解调对比解调部分代码分两段先相干后非相干% 相干解调 z1 ddemod(y, Fc, Fd, Fs, fsk/eye, M, df); % 非相干解调 z2 ddemod(y, Fc, Fd, Fs, fsk/eye/noncoh, M, df);ddemod的第五个参数fsk/eye表示相干解调并输出眼图fsk/eye/noncoh表示非相干。相干解调需要本地载波与发送载波同频同相代码里dmod和ddemod用同一组Fc、Fd、Fs所以相位是对齐的。如果自己改载频两边必须同步改否则相干解调出来的全是错码。非相干解调不依赖相位所以对载波相位偏差不敏感但抗噪声性能差一些。代码里把两种解调结果都画出来对比% 误码率统计 [errorSym1, ratioSym1] symerr(x, z1); [errorSym2, ratioSym2] symerr(x, z2);symerr返回错误符号数和误码率。在SNRpBit60时相干解调误码率通常为 0非相干可能有个别错码。把SNRpBit降到 10 以下两者差距会明显拉大。3. 运行环境与实操步骤从零跑通第一张波形图3.1 MATLAB 版本与工具箱依赖这份代码基于 MATLAB 7.0 编写但实际在 R2015b 之后的版本也能跑因为dmod、ddemod、awgn、symerr、rcosine、rcosflt这些函数属于 Communications Toolbox 和 Signal Processing Toolbox。如果只装了基础 MATLAB这些函数会报未定义。检查方法很简单在命令行输入ver comm ver signal如果输出里有 Communications Toolbox 和 Signal Processing Toolbox 的版本信息就可以直接跑。如果没有需要先安装对应工具箱。常见做法是装完整版 MATLAB或者用comm开头的替代函数重写调制解调部分。3.2 脚本分段执行与变量检查不建议一次性全选运行因为中间有 10 个 figure全跑完窗口会堆满。我一般分段执行第一步只跑信源和调制Fc10; Fs40; Fd1; NFs/Fd; df10; numSymb25; M2; x randsrc(numSymb, 1, [0:M-1]); y dmod(x, Fc, Fd, Fs, fsk, M, df);跑完检查x是不是 0 和 1 的列向量y的长度是不是numSymb*Fs。如果y全是 0说明dmod参数写错了常见是Fd和Fs位置颠倒。第二步加噪声并画图SNRpBit60; SNRSNRpBit/log2(M); seed[12345 54321]; randn(state, seed(2)); y awgn(y, SNR-10*log10(0.5)-10*log10(N), measured, [], dB); figure(3); plot(y(1:200)); title(加入高斯白噪声后的已调信号);看波形有没有明显的正弦疏密变化。如果噪声把信号完全淹没把SNRpBit调大比如 80。第三步解调并对比z1 ddemod(y, Fc, Fd, Fs, fsk/eye, M, df); z2 ddemod(y, Fc, Fd, Fs, fsk/eye/noncoh, M, df); [errorSym1, ratioSym1] symerr(x, z1); [errorSym2, ratioSym2] symerr(x, z2); disp([相干误码率: , num2str(ratioSym1)]); disp([非相干误码率: , num2str(ratioSym2)]);如果误码率不是 0先检查x和z1的长度是否一致。ddemod输出可能比x短几个码元因为滤波有延迟。代码里用z1(1:numPlot)截取前 15 个对比就是避开延迟段。3.3 波形与眼图观察要点眼图是判断解调质量最直观的工具。代码里画了相干和非相干解调后的眼图figure(4); z1 ddemod(y, Fc, Fd, Fs, fsk/eye, M, df); title(相干解调后的信号的眼图);眼图张开越大说明码间干扰越小判决门限越清晰。如果眼图闭合常见原因是信道噪声太大或者滤波器带宽不对。代码后面用升余弦滤波器滤除带外噪声Delay3; R0.5; [yf, tf] rcosine(Fd, Fs, fir, R, Delay); [yo2, to2] rcosflt(y, Fd, Fs, filter, yf);R0.5是滚降系数Delay3是滤波器延迟。升余弦滤波后眼图会明显张开误码率也会下降。如果R设成 0滤波器理想但不可实现设成 1带宽大但频带利用率低。0.5 是常用折中。3.4 误码率曲线绘制与 SNR 扫描代码最后用simbasebandex画误码率曲线figure(8); simbasebandex([0:1:5]); title(相干解调后误码率统计);simbasebandex是 MATLAB 自带的基带仿真示例函数输入是 SNR 范围。它会自动跑不同信噪比下的误码率并画曲线。如果这个函数报错常见原因是路径里没有comm示例文件夹。我一般会自己写循环代替SNR_vec 0:2:16; ber_coh zeros(size(SNR_vec)); ber_noncoh zeros(size(SNR_vec)); for i 1:length(SNR_vec) y_noisy awgn(y, SNR_vec(i), measured); z1 ddemod(y_noisy, Fc, Fd, Fs, fsk, M, df); z2 ddemod(y_noisy, Fc, Fd, Fs, fsk/noncoh, M, df); [~, ber_coh(i)] symerr(x, z1(1:numSymb)); [~, ber_noncoh(i)] symerr(x, z2(1:numSymb)); end semilogy(SNR_vec, ber_coh, b-o, SNR_vec, ber_noncoh, r-s); legend(相干解调, 非相干解调); xlabel(SNR (dB)); ylabel(误码率); grid on;这样跑出来的曲线相干解调在低信噪比下明显优于非相干和教材结论一致。4. 核心代码逐段拆解调制、信道、解调、滤波4.1 信源产生与码元映射信源用randsrc产生随机整数numSymb 25; M 2; x randsrc(numSymb, 1, [0:M-1]);randsrc第一个参数是行数第二个是列数第三个是符号集。[0:M-1]就是[0 1]等概率产生。如果想改成非等概率可以写成[0 1; 0.7 0.3]表示 0 出现概率 0.7。不过 2FSK 课设一般用等概率。产生的x是列向量长度 25。后面画图时用x(1:numPlot)取前 15 个numPlot15。如果numSymb小于numPlot会报索引越界所以改numSymb时要同步改numPlot。4.2 2FSK 调制与频率间隔设置调制核心是dmody dmod(x, Fc, Fd, Fs, fsk, M, df);这里Fc10是中心频率df10是频率间隔所以两个载频是Fc-df/25和Fcdf/215。dmod内部用开关法产生 2FSK当码元为 0 时输出频率 5 的正弦波为 1 时输出频率 15 的正弦波。相邻码元之间相位不连续因为两个频率源独立。如果想改成相位连续的 2FSK需要自己写积分器不能用dmod的fsk模式。常见做法是用fsk加cont参数但 MATLAB 版本不同支持不一样。我一般直接手写t (0:length(x)*N-1)/Fs; phase 2*pi*(Fc df/2*(2*x-1))*t; y_cont cos(phase);这样相位连续但代码复杂课设一般用开关法就够了。4.3 高斯信道噪声功率换算awgn函数的信噪比参数容易搞混SNRpBit 60; SNR SNRpBit / log2(M); y awgn(y, SNR - 10*log10(0.5) - 10*log10(N), measured, [], dB);SNRpBit是每比特信噪比SNR是每符号信噪比。2FSK 一个符号一个比特所以SNR SNRpBit。10*log10(0.5)是因为单极性信号平均功率是 0.510*log10(N)是因为awgn按采样点算信噪比而每个码元有 N 个采样点。如果直接写awgn(y, SNRpBit, measured)噪声功率会偏大因为没做采样点折算。我见过有人把SNRpBit设成 60 还嫌噪声小就是漏了10*log10(N)。4.4 相干解调与抽样判决实现相干解调用ddemodz1 ddemod(y, Fc, Fd, Fs, fsk/eye, M, df);ddemod内部流程是带通滤波 → 与本地载波相乘 → 低通滤波 → 抽样判决。fsk/eye会额外画眼图。抽样判决的门限是 0.5因为单极性信号幅度是 0 和 1。如果想手动实现相干解调可以拆开写% 带通滤波 [b1, a1] butter(4, [(Fc-df)/Fs*2, (Fcdf)/Fs*2]); y_bp filter(b1, a1, y); % 相干相乘 t (0:length(y_bp)-1)/Fs; y_mix y_bp .* cos(2*pi*Fc*t); % 低通滤波 [b2, a2] butter(4, Fd/Fs*2); y_lp filter(b2, a2, y_mix); % 抽样判决 y_sample y_lp(1:N:end); z_manual y_sample 0.5;这样每一步都可见方便调试。但要注意滤波器延迟y_sample可能和x不对齐需要手动截取。4.5 升余弦滤波与眼图生成升余弦滤波在代码最后Delay 3; R 0.5; [yf, tf] rcosine(Fd, Fs, fir, R, Delay); [yo2, to2] rcosflt(y, Fd, Fs, filter, yf);rcosine生成升余弦滤波器系数rcosflt对信号滤波。Delay3表示滤波器延迟 3 个码元R0.5是滚降系数。滤波后信号yo2比y长因为卷积有拖尾。眼图用eyediagrameyediagram(yo2, N); title(加入高斯白噪声后的已调信号的眼图);N40是每码元采样点数眼图横轴显示 2 个码元。如果眼图线条很粗说明噪声大如果眼图闭合说明码间干扰严重。升余弦滤波后眼图应该明显张开。5. 避坑与常见问题课设代码跑不通的 5 个原因5.1 报错“Undefined function dmod”现象运行到dmod那一行MATLAB 提示未定义函数。原因没装 Communications Toolbox或者工具箱路径没加载。解决命令行输入ver查看已安装工具箱。如果没有 Communications Toolbox可以用fskmod代替dmod但参数顺序不同。或者手写调制t (0:length(x)*N-1)/Fs; freq Fc df/2*(2*x-1); y cos(2*pi*freq.*t);这样不依赖工具箱但要注意freq和t的维度匹配。5.2 波形全是直线或全是噪声现象figure(2)画出来是一条直线或者figure(3)全是毛刺。原因Fs和Fc关系不对或者awgn信噪比换算错误。解决检查Fs 4*Fc。如果Fs40、Fc10刚好 4 倍可以。如果Fc改成 20Fs必须至少 80。噪声问题检查SNR - 10*log10(0.5) - 10*log10(N)这一串少减一项噪声就会偏大。5.3 误码率始终为 0.5现象symerr返回的误码率接近 0.5说明解调输出和原信号完全不相关。原因相干解调时本地载波相位反了或者ddemod的Fc和dmod不一致。解决确保dmod和ddemod用同一组Fc、Fd、Fs、df。如果手动改过载波两边同步改。另外检查z1和x的长度如果z1比x短很多截取对齐后再算误码率。5.4 眼图不显示或显示异常现象eyediagram报错或者画出来空白。原因输入信号长度不是N的整数倍或者N设置错误。解决eyediagram要求输入长度是每码元采样点数的整数倍。yo2经过滤波后长度可能不是N的整数倍可以截取yo2 yo2(1:floor(length(yo2)/N)*N); eyediagram(yo2, N);另外N必须等于Fs/Fd如果Fs或Fd改了N要同步改。5.5 误码率曲线不下降现象simbasebandex画出来的曲线随信噪比增加不降反升或者一直平着。原因simbasebandex是示例函数内部用的信号和当前脚本不一致。解决自己写循环扫描信噪比如 3.4 节所示。注意每次循环要重新加噪声不能复用同一个y。另外symerr的第二个参数要截取到和x等长否则长度不一致会报错。6. 二次开发与参数调优把这套代码改成你自己的课设6.1 改调制方式从 2FSK 到 2ASK 或 2PSK这份代码的调制解调部分用的是dmod/ddemod的fsk模式。如果想改成 2ASK把fsk换成askdf参数去掉y dmod(x, Fc, Fd, Fs, ask, M); z1 ddemod(y, Fc, Fd, Fs, ask/eye, M);2PSK 类似换成psk。但要注意 2PSK 有相位模糊问题相干解调可能输出反相误码率会接近 0.5。常见做法是加差分编码或者用非相干解调。6.2 改信道模型从高斯信道到瑞利衰落代码里信道是awgn只加高斯白噪声。如果想模拟无线信道可以加瑞利衰落% 瑞利衰落 h (randn(length(y),1) 1j*randn(length(y),1))/sqrt(2); y_fading y .* abs(h); y_noisy awgn(y_fading, SNR, measured);这样误码率曲线会比高斯信道差很多因为衰落会导致深度凹陷。课设如果要求“无线信道”可以加这一段。6.3 改性能指标从误码率到误符号率symerr算的是误码率因为 2FSK 一个符号一个比特。如果改成 4FSKM4一个符号两个比特symerr算的是误符号率。要算误比特率需要把符号转成比特再比较x_bit de2bi(x, log2(M), left-msb); z_bit de2bi(z1, log2(M), left-msb); [~, ber] biterr(x_bit, z_bit);这样统计出来的才是误比特率。课设如果要求“误码率”一般指误比特率要注意区分。6.4 参数扫描与自动化跑图手动改参数跑图效率低我一般写个循环自动扫描SNR_vec 0:2:20; ber_coh zeros(size(SNR_vec)); ber_noncoh zeros(size(SNR_vec)); for i 1:length(SNR_vec) y_noisy awgn(y, SNR_vec(i), measured); z1 ddemod(y_noisy, Fc, Fd, Fs, fsk, M, df); z2 ddemod(y_noisy, Fc, Fd, Fs, fsk/noncoh, M, df); [~, ber_coh(i)] symerr(x, z1(1:numSymb)); [~, ber_noncoh(i)] symerr(x, z2(1:numSymb)); end semilogy(SNR_vec, ber_coh, b-o, SNR_vec, ber_noncoh, r-s);这样一次跑完所有信噪比点曲线直接出来。注意每次循环要重新加噪声不能复用同一个y_noisy否则噪声相关曲线会失真。6.5 代码结构优化从脚本到函数原始代码是脚本变量全在 workspace 里容易冲突。我一般会改成函数function [ber_coh, ber_noncoh] fsk_sim(SNR_vec, numSymb) Fc10; Fs40; Fd1; NFs/Fd; df10; M2; x randsrc(numSymb, 1, [0:M-1]); y dmod(x, Fc, Fd, Fs, fsk, M, df); ber_coh zeros(size(SNR_vec)); ber_noncoh zeros(size(SNR_vec)); for i 1:length(SNR_vec) y_noisy awgn(y, SNR_vec(i), measured); z1 ddemod(y_noisy, Fc, Fd, Fs, fsk, M, df); z2 ddemod(y_noisy, Fc, Fd, Fs, fsk/noncoh, M, df); [~, ber_coh(i)] symerr(x, z1(1:numSymb)); [~, ber_noncoh(i)] symerr(x, z2(1:numSymb)); end end这样调用fsk_sim(0:2:20, 1000)就能跑变量不污染 workspace也方便批量跑不同参数。6.6 一个我踩过的坑滤波器延迟导致误码率虚高有一次我把ddemod输出的z1直接和x算误码率结果误码率一直 0.3 下不去。后来发现ddemod内部有滤波器延迟z1的前几个码元是滤波暂态和x不对齐。解决办法是截取对齐delay 3; % 根据滤波器阶数估计 z1_aligned z1(delay1:delaynumSymb); [~, ber] symerr(x, z1_aligned);延迟多少取决于滤波器阶数和Fs/Fd。我一般先画x和z1的对比图肉眼找对齐点再截取。从那以后我每次算误码率前都强制走一遍对齐检查再也不敢直接symerr(x, z1)了。希望这份拆解能帮你把课设跑通少走点弯路。本文还有配套的精品资源点击获取
RELATED

相关推荐

Claude Code模板体系实战:CLAUDE.md与Slash Commands提效指南

Claude Code模板体系实战:CLAUDE.md与Slash Commands提效指南

说真的,第一次接触 Claude Code 的时候我还是比较谨慎的,命令行式 AI 编程工具听起来很酷,但总觉得差点什么。直到我花了两周时间把所有重复性的工程操作沉淀成了一套模板体系,这工具才真正从"玩具"变成了每天离不开的生…

📅 2026/10/17 17:10:08
TensorFlow 2手写线性回归:从GradientTape到可调试模型

TensorFlow 2手写线性回归:从GradientTape到可调试模型

1. 这不是教科书里的公式推导,而是你明天就能跑通的线性回归实战“TensorFlow 2 线性回归”这八个字,看起来像极了某门研究生课程的作业题——但如果你真把它当成理论题来解,大概率会在写完import tensorflow as tf后卡住半小时:接…

📅 2026/10/17 17:05:08
用Python造模拟测试数据,验证ITKMontage自动拼接模块

用Python造模拟测试数据,验证ITKMontage自动拼接模块

简介:为ITKMontage自动拼接模块准备的测试数据,面向图像拼接算法开发者与医学影像处理研究人员,可用于验证自动拼接流程的参数设置与算法鲁棒性。资源包共744个文件,大小约964.58MB,主体为bmp、jpg、png、tif等二维图像…

📅 2026/10/17 17:05:08
MORE NEWS

更多资讯

📰

Kafka接入AI实战:从Lag排查到Function Calling自动化运维

1. 为什么要把Kafka接进AI:这个标题背后要解决的真实问题先聊点实在的。做Kafka运维和研发的朋友,应该都有过这种经历:半夜被ConsumerGroup的Lag告警叫起来,打开电脑连上跳板机,一条一条查kafka-consumer-groups.sh --…

📰

harness 工程化实践:用 AGENTS.md 与 Makefile 把 TaoToken 接入 CI 流水线

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

大模型推理-基于prefill和decode阶段特性,PD分离,势在必行!TaoToken统一Key下的PD分离部署大纲

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

2026年5月开源模型Token服务实测榜:TaoToken统一Key下DeepSeek/GLM/Kimi三层架构性能拆解

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

Codex 下载与本地部署实战:从安装到运行全流程指南(TaoToken 统一 Key 接入版)

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

QML自制数字软键盘:向TextInput/TextField键入数字并实现左移右移删除

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬