
简介一套面向通信与信号处理方向的MATLAB调制仿真代码包完整覆盖BASK、BFSK、DSB、SSB四种经典数字调制方式并提供可运行脚本与可视化结果。压缩包共60个文件以20个.m脚本为核心包含调制解调函数、fft频谱分析、图形绘制等代码同时附有15张jpg和4张png运行结果截图、10个fig可编辑图形文件便于对照波形和频谱理解原理8个asv自动备份文件可辅助排查修改过程中的问题少量bmp素材用于界面背景等场景。整个资源包仅2.27MB轻巧便于下载目前已有212人浏览学习。读者拿到后可直接在MATLAB2019a中运行主程序得到BASK、BFSK、DSB、SSB的仿真波形并可通过fig文件交互调整参数重新生成图像适合作为课程设计、实验报告或毕业设计预研的参考资料尤其有利于快速验证调制解调算法正确性。1. 为什么把这四种调制放到同一个仿真包里BASK、BFSK、DSB、SSB这四种调制方式表面上看分属两个阵营前两个是数字键控调制后两个是模拟线性调制。但在MATLAB仿真层面它们的代码骨架高度相似都是先生成基带信号再与载波相乘或做频率切换最后用频谱和波形验证结果。这个包里同时给出AskForm.m、FskForm.m、DsbForm.m、SsbForm.m以及对应的.fig界面文件等于把通信原理课程里最容易混淆的四章内容拉到了同一套参数体系下对比省去了自己搭测试台的时间。我通常建议本科和研究生阶段的教研学习直接在这种整合型仿真包上改参数而不是从零开始写四个独立的工程。原因很实际单独实现DSB和SSB时很多人会忽略两者之间只是差一个希尔伯特变换单独实现BASK和BFSK时又容易把键控调制和线性调制的带宽判定标准搞混。把这四个放在一起只要改几行代码就能看到波形和频谱的连续演化理解成本反而低。不过这套包的文件组织比较零散既有脚本也有GUI直接跑容易迷路下面我按调制类型把代码结构和参数逻辑拆开讲。2. BASK 与 BFSK键控调制的核心代码与参数选择2.1 键控调制的本质用码元去切换载波状态BASK二进制幅移键控和BFSK二进制频移键控都属于数字调制区别在于用基带码元直接控制载波的哪个参数。BASK用高电平对应发送载波、低电平对应不发送等效于OOKBFSK则用两个不同频率的载波分别对应码元0和1。理解这一点后写代码的逻辑就变成先把随机比特流变成基带波形再按码元逐段生成对应载波。包里的FskForm.m就是典型的逐码元拼接思路核心代码大致如下fs 8000; % 采样率 8 kHz Rb 100; % 码元速率 100 bps Tb 1 / Rb; % 码元宽度 10 ms t 0 : 1/fs : Tb - 1/fs; % 单个码元的时间轴 f1 1000; % 码元1对应的载波频率 f2 2000; % 码元0对应的载波频率 bits randi([0 1], 1, 50); bfsk zeros(length(bits), length(t)); for k 1 : length(bits) if bits(k) 1 bfsk(k, :) cos(2 * pi * f1 * t); else bfsk(k, :) cos(2 * pi * f2 * t); end end bfsk_sig reshape(bfsk., [], 1).;这段代码里有两个参数需要重点说明。第一是采样率fs和载波频率fc的倍数关系fs等于8000而f1、f2分别取1000和2000每个载波周期采8个点画波形时足够平滑第二是f1和f2都是Rb的整数倍保证码元切换瞬间载波相位是连续的否则频谱上会出现明显的杂散分量。如果你要改成自己的参数最简单的做法是保持 f1 10 * Rb、f2 20 * Rb 这样的比例关系再反推fs至少取最大载波频率的8倍。2.2 BASK 的两种实现方式与带宽差异BASK在代码上比BFSK更短核心就是基带信号和载波直接相乘fc 1000; ask_sig bits_up .* cos(2 * pi * fc * t_total);其中bits_up是经过零阶保持后的单极性不归零波形。这里有个容易混淆的点BASK的带宽等于2Rb而BFSK的带宽约等于2(Rb Δf)Δf是两个载波频率的差值。意味着BFSK占用更宽的频带换来的是更强的抗噪声能力。你在示波器上看BASK波形时会发现它的包络直接反映了比特流形状而BFSK的包络恒定为1只能通过过零率来区分码元。下表是这两种键控调制在仿真时最常用的参数对照照着设置基本不会出问题参数BASKBFSK载波频率 fc建议 ≥ 10 * Rbf1、f2 均建议 ≥ 10 * Rb采样率 fs≥ 10 * fc≥ 10 * max(f1, f2)带宽估算2 * Rb2 * (Rb Δf)判决方式包络检波 / 相干解调过零检测 / 相干解调频谱特征中心谱线 双边带双峰谱线2.3 运行后看什么波形、频谱和包络跑完AskForm.m和FskForm.m之后建议先观察三个地方。第一是时域波形中码元切换点是否存在毛刺如果有多半是载波频率不是码元速率的整数倍第二是功率谱密度图中主瓣宽度是否和上面的公式估算一致不一致就检查是不是加了升余弦脉冲成型第三是BFSK的频谱是否出现了两个明显的谱峰两个峰之间的间隔应接近Δf。包里的运行结果BASK.PNG和运行结果BFSK.PNG就是典型的正确输出对照着看能更快发现自己的参数改错在哪里。3. DSB 与 SSB从双边带到希尔伯特变换3.1 DSB 的数学结构与载波抑制DSB双边带抑制载波调制是线性调制的基准时域表达式为 s(t) m(t) × cos(ωc t)频域结果是基带频谱被搬移到载波两侧没有载波分量。包里DsbForm.m的核心实现非常直接fs 20000; fc 3000; t 0 : 1/fs : 0.02 - 1/fs; mt sin(2 * pi * 300 * t) 0.5 * sin(2 * pi * 700 * t); dsb_sig mt .* cos(2 * pi * fc * t); % 双边带调制 N length(dsb_sig); freq (-N/2 : N/2 - 1) * fs / N; DSB_SPEC fftshift(abs(fft(dsb_sig)) / N);这里调制信号用了两个单音的叠加是为了方便在频谱上同时观察两个边带是否对称。运行后你会在频谱图上看到载波频率两侧各有一个与基带频谱形状一致的峰而且载波频率3000Hz处没有谱线这就是抑制载波的特征。很多初学者会用dsb_sig (mt 1) .* cos(2*pi*fc*t)来保留载波那其实是AM调制不是DSB两者在频谱和包络上有本质区别。3.2 SSB 的希尔伯特实现为什么要 90 度移相SSB调制是在DSB基础上滤除一个边带。滤波器法直观但阶数高、信号延迟大仿真中更推荐用希尔伯特变换实现。其数学原理是解析信号的频谱只保留正频率部分取实部后就得到了单边带信号。USB上边带和LSB下边带的区别只在于希尔伯特变换结果的符号。代码对应包里的ussb_mod.m和lssb_modmod.m核心如下mt sin(2 * pi * 300 * t) 0.5 * sin(2 * pi * 700 * t); ht imag(hilbert(mt)); % 希尔伯特变换即 90 度移相 usb_sig mt .* cos(2 * pi * fc * t) - ht .* sin(2 * pi * fc * t); lsb_sig mt .* cos(2 * pi * fc * t) ht .* sin(2 * pi * fc * t);这段代码里的hilbert函数返回的是解析信号实部是原始信号本身虚部才是希尔伯特变换结果所以用imag取虚部。乘上cos载波再减去ht乘以sin载波得到的是上边带把减号改成加号就变成下边带。这个90度移相的过程在频谱上等价于把负频率分量抵消、正频率分量加倍因此输出信号的频谱只有载波一侧有分量。3.3 SSB 和 DSB 频谱对比的验证方法写完代码后建议把DSB和SSB的频谱画在同一张图上验证。你可以先算DSB的总功率再算USB单独的总功率会发现SSB信号功率只有DSB的一半这就是单边带节省功率的直接体现。再看带宽DSB占用的带宽是基带信号最高频率的两倍而SSB的带宽等于基带信号最高频率本身频谱图上载波只有一侧有信号。包里的运行结果DSB.PNG和运行结果SSB.PNG分别展示了这两种频谱形态如果自己的图里SSB两侧都有谱线说明希尔伯特变换用成了原始信号或者把imag写成了real这是最常见的错误。4. .m 与 .fig 的配合这套仿真包的 GUI 运行方式4.1 文件结构哪些是主程序哪些是辅助函数这个压缩包里除了四个调制主脚本外还有大量.fig文件和辅助脚本刚开始容易分不清运行顺序。我建议把文件分成三层来看第一层是四个核心调制脚本AskForm.m、FskForm.m、DsbForm.m、SsbForm.m第二层是辅助计算函数fftseq.m、posisi_grafik.m、kanale.m第三层是界面文件SysFirstF.m、Grafik.m和各模块的.fig文件。平时学习直接运行四个核心脚本即可GUI文件是为做演示和课设答辩准备的。下面这个表列了主要的fig文件和对应脚本的关系方便你在MATLAB里按图索骥fig 文件对应脚本功能AskForm.figAskForm.mBASK 调制波形与频谱展示FskForm.figFskForm.mBFSK 调制波形与频谱展示DsbForm.figDsbForm.mDSB 调制波形与频谱展示SsbForm.figSsbForm.mSSB 上下边带对比展示SysFirstF.figSysFirstF.m总控界面汇总各模块入口M_QAM.figM_QAM.m多进制 QAM 扩展实验4.2 GUI 回调里的参数传递handles 的作用如果你选择在GUI界面里跑仿真需要理解MATLAB GUI最核心的handles结构体。简单说它是所有控件共享的一个数据容器你在回调函数里写入参数另一个回调函数就能读出来。以FskForm为例典型的做法是在OpeningFcn里初始化参数在运行按钮回调里读取参数再调用核心函数function FskForm_OpeningFcn(hObject, eventdata, handles, varargin) handles.fs 8000; handles.fc 1800; handles.Rb 100; handles.output hObject; guidata(hObject, handles); function btnRun_Callback(hObject, eventdata, handles) fs handles.fs; % 从 handles 读取采样率 fc handles.fc; Rb handles.Rb; sim_BFSK(fs, fc, Rb); % 调用核心仿真函数这里的逻辑说明是guidata(hObject, handles)把handles写回GUI的全局存储区这样下次任何回调函数拿到的handles都是最新版本。btnRun_Callback里先取参数再执行仿真界面上的可编辑文本框通过str2double(get(handles.editFs, String))读取这里就不展开了。如果你在GUI里改了参数但波形没变化百分之八十是忘了在参数修改回调里调用guidata更新handles。4.3 参数输入错误时怎么看报错信息运行GUI时最常见的报错是矩阵维度不匹配比如采样率、码元数、载波频率三者算出来的时间轴长度对不上。如果你看到Matrix dimensions must agree优先检查三个变量的关系总采样点数是否等于length(t)t的步长是否为1/fs以及载波表达式中t和f的维度是否一致。另一些报错出现在fftseq.m中这个函数封装了补零和频谱归一化逻辑报错通常是输入信号长度不是2的幂把信号尾部补零到2^nextpow2(N)即可解决。5. 解调端验证从波形观察到误码率计算5.1 包络检波与相干解调的仿真差异调制代码有了还需要在接收端做验证才能说明调制是正确的。BASK使用包络检波最直观对接收信号取绝对值再过低通滤波器恢复出的包络就是基带码元序列。而BFSK更推荐用相干解调或过零检测因为它的幅度不携带信息。下面这段代码演示了BASK的包络检波% 接收端处理 rx_ask awgn(ask_sig, 15, measured); % 加 15dB 高斯白噪声 env abs(hilbert(rx_ask)); % 希尔伯特变换取包络 [b, a] butter(3, Rb / (fs/2), low); % 低通滤波 baseband_rx filter(b, a, env); % 滤波后得到基带信号这里用hilbert取包络是因为它比二极管检波器的仿真模型更简洁本质是把实信号转成解析信号再取模。低通滤波器的截止频率设为Rb/(fs/2)意思是保留码元速率以下的基带分量滤除二倍载波分量。这个处理同样适用于BASK的相干解调差别只是相干解调需要先本地恢复载波再乘回去。5.2 用 BER 曲线验证键控调制的可行性如果要做误码率分析包内的M_QAM.m里有完善的误码统计框架可以直接借用到BASK和BFSK上。更快的做法是用MATLAB自带的berawgn函数画理论曲线作参照EbNo 0 : 2 : 12; ber_ask berawgn(EbNo, pam, 2); % 2PAM 等效于 BASK ber_fsk_coh berawgn(EbNo, fsk, 2, coherent); ber_fsk_non berawgn(EbNo, fsk, 2, noncoherent); semilogy(EbNo, ber_ask, -o, EbNo, ber_fsk_coh, -s, EbNo, ber_fsk_non, -d); grid on; xlabel(Eb/No (dB)); ylabel(BER); legend(BASK, BFSK 相干, BFSK 非相干);这段代码里的逻辑是BASK二进制幅移键控在误码率上和2PAM一致所以直接复用pam类型BFSK区分相干和非相干差别在于检测时是否依赖载波相位。运行后会看到一条典型的瀑布曲线其中BASK比BFSK相干解调大约差3dB这就是幅度调制不如频率调制抗噪的理论依据。把你的蒙特卡洛仿真散点叠在这条理论曲线上就能验证仿真代码是否写对。5.3 眼图和星座图的辅助判断在仿真包里做解调验证眼图是另一个快速判断手段。对恢复后的基带信号按码元周期Tb做重叠切片绘制眼图如果眼睛张开明显且中心位置清晰说明码间串扰小。这里可以直接调用包内提供的posisi_mata_kon.m它封装了眼图的绘制逻辑eyediagram(baseband_rx, 2 * fs / Rb); % 每码元绘制两个周期参数中第二个参数是每个码元的采样点数取2表示每个码元显示两个采样周期适合观察采样时刻的裕量。如果眼图模糊成一团先检查低通滤波器截止频率是否设置过高把无关的带外噪声放进了判决电路。6. 进阶调试用频谱对齐快速定位参数设置错误最后一个分享是调试技巧作用是当代码能跑但波形不对时用频谱分析快速定位是哪一步设错了。这个技巧来自包里的fftseq.m结合实际的用法算是把频谱工具用到极致的思路。第一步先做semi-analytic半解析预检。不运行完整调制流程用10个码元、一个载波周期8个采样点这样的小规模参数直接输出调制信号的频谱并把理论带宽边界画成垂直线叠加在上面用matlab脚本快速确认主瓣位置是否落在预期范围内hold on; xline(fc - Rb, --r); % 理论下边带边界 xline(fc Rb, --r); % 理论上边带边界如果频谱能量明显超出红线范围说明码元波形上升下降沿太陡需要在前端加升余弦脉冲成型。BASK最容易出现这种问题因为矩形码元的频谱旁瓣衰减慢。第二步检查BFSK的频率切换是否真正连续。在频谱图上观察两个谱峰之间是否存在非预期的第三簇频谱如果存在说明f1和f2与码元速率Rb之间不是整数倍关系导致码元切换瞬间载波相位跳变。修正方法就是把f1、f2改成Rb的整数倍例如Rb100时取f11500、f22500整数倍关系让切换点相位天然对齐。第三步对SSB信号做一次反向验证。用hilbert函数重建解析信号检查负频率分量的残留是否低于-40dBanalytic hilbert(usb_sig); neg_suppress 20 * log10(norm(fft(analytic)) / norm(fft(usb_sig))); fprintf(负频率抑制比: %.2f dB\n, neg_suppress);这个数值低于-40dB说明希尔伯特变换实现正确上边带信号中没有混入下边带分量。如果数值偏大检查你的ht是否真的来自imag(hilbert(mt))以及载波频率与基带信号频率之间是否发生了频谱混叠混叠时上边带和下边带无法通过移相分离抑制比自然提高。这套检查流程可以在不打开示波器的情况下用MATLAB命令窗口快速定位调制链路中80%以上的参数设置问题。本文还有配套的精品资源点击获取