2ASK/OOK调制解调误码率仿真:MATLAB完整代码与原理详解
发布时间:2026/8/31 9:38:47来源:尧图网络
简介本资源是一份面向通信工程专业本科生、研究生及数字通信初学者的2ASK调制解调全流程MATLAB仿真教学代码聚焦基带信号生成、载波调制、信道加噪、相干解调与判决等核心环节系统解决理论理解与仿真实践脱节问题。压缩包共4个文件3个.m函数1个说明文档总大小仅4KB主程序BASK_no_rcosine.m完整实现二进制序列生成、上采样、乘法调制、高斯白噪声叠加、本地载波同步解调、低通滤波lowpass.m、抽样判决并同步输出时域波形、双边/单边频谱image_frequencyspectrum.m、功率谱密度、星座图、眼图及理论/仿真误码率对比曲线所有代码逐行注释逻辑分层清晰支持ebn0单点调试如ebn010以观察中间过程细节。目前已有1548人学习下载代码已在MATLAB 2018a与2023b版本实测无报错是深入掌握2ASK原理、提升通信系统建模能力的高实用性学习材料。 先说个题外话。我当年做通信原理课设的时候导师丢给我一句“把2ASK调制的误码率曲线仿真出来”然后就忙去了。我抱着教材啃了两天上网上翻到的代码要么精炼得像火星文要么跑一半就报错最终自己一点一点把整个流程啃透了。现在我把这套经过反复调试的完整代码和踩坑心得整理出来希望你能少走点弯路。这篇文章不是给你贴一段黑盒代码就完事而是带着你把每一步的原理、参数怎么定、滤波器怎么选、误码率怎么统计这一整套逻辑捋清楚。无论你是刚学通信原理的本科生还是在准备复试、做课程设计的研究生只要跟着文章走一遍2ASK的仿真你基本能拿捏得死死的。1. 动手之前先把2ASK的原理和仿真思路盘清楚1.1 信号模型OOK到底是怎么工作的2ASK的全称是二进制振幅键控Amplitude Shift Keying本质就是用一个二进制基带信号去控制正弦载波的幅度。最常见的实现方式是OOKOn-Off Keying也就是发送二进制“1”的时候载波正常输出发送二进制“0”的时候载波输出为0相当于“断开”。从数学上看发送信号可以写成s(t) A * m(t) * cos(2πfc*t)其中m(t)是单极性不归零NRZ基带波形取值为1或0cos(2πfc*t)是载波A是载波幅度。这个模型也直接对应了第一种实现方式——乘法器法。另一种实现方式叫键控法就是用开关电路在载波源和地之间切换模拟里两者本质是一样的都是让载波“有”和“没有”两种状态。这里有个细节可能让你在教材和论文里看到不同的公式有些教材里2ASK信号还写成A * m(t) * cos(2πfc*t)其中m(t)取0或1有些写成s0(t)Acos(...)和s1(t)0。两种写法在原理上没有区别关键看你怎么定义二进制与电平的映射。在仿真中我们统一采用“1对应有载波0对应无载波”的映射这样最直观后期判决门限也好设计。1.2 参数规划采样率、载频、每个码元的采样点数怎么定仿真开始前先别急着敲代码把参数想清楚可以给你省下大把调试时间。核心参数有四个采样频率fs、载波频率fc、码元速率Rb即波特率2ASK下等于比特率、以及每个码元内的采样点数Ns。它们之间的关系是Ns fs / Rb。我给出的参数组合是参数数值说明fs10000 Hz采样频率fc1000 Hz载波频率Rb100 bps码元速率Ns100每个码元的采样点数N_bits2000仿真比特总数为什么这样选首先载波频率要远低于采样率同时不能太低导致一个码元内载波周期太少。fc 1000 Hz时每个码元恰有10个完整载波周期Ns / (fs/fc) 100 / 10 10画波形时能清楚看到OOK的“断开”和“连上”效果解调时低通滤波也容易把载波残留滤干净。其次Rb取得小一点仿真时间不长但波形数量足够画图观察。注意取Ns 100还带来了一个好处解调端的“积分清洗”或移动平均滤波器可以直接用长度为Ns的窗口不需要动态调整窗长。1.3 仿真流程总览整个仿真流程是经典的“发—传—收—判”四段式打通用一句话描述就是随机比特 → 电平映射 → 载波调制 → 加高斯白噪声 → 解调相干或非相干→ 抽样判决 → 对比误码率对应到代码里我会分模块写结构化程度高一点。后面每一节都有可直接运行的片段你也可以把整段代码复制到一个脚本里直接执行。2. 调制端完整代码从比特流到频带波形2.1 参数设置与初始化%% 2ASK调制解调仿真 clear; close all; clc; rng(42); % 固定随机种子保证实验结果可复现 % ------- 系统参数 ------- fs 10000; % 采样频率 10kHz fc 1000; % 载波频率 1kHz Rb 100; % 码元速率 100 bps Ns fs / Rb; % 每个码元的采样点数100 N_bits 2000; % 仿真比特数 N0_bits 100; % 前100个比特用于观察波形这里我建议固定随机种子rng(42)在做课程设计或写实验报告时很有用。你对比不同信噪比下误码率时如果每次随机比特都变严格来说也能通过大数定理得到稳定结果但固定种子可以让你每次跑出来的误码率是同一个具体数方便复现实验数据报告里也更好描述。2.2 生成随机比特流并映射为基带波形% ------- 发送端 ------- bits randi([0, 1], 1, N_bits); % 等概率生成0/1比特流 % 将比特流映射为单极性NRZ基带波形1变高电平0变低电平 baseband repelem(bits, Ns);repelem是MATLAB里一个很好用的函数它把每个元素重复Ns次。比如bits [1 0 1]Ns 4repelem之后得到[1 1 1 1 0 0 0 0 1 1 1 1]正好就是NRZ的波形采样。如果你用的MATLAB版本比较老R2015a之前没有repelem函数可以换成reshape(ones(Ns,1)*bits, 1, [])效果一样但可读性差一些建议新版本直接repelem。2.3 载波生成与2ASK调制% 时间轴总采样点数 N_bits * Ns t (0 : N_bits * Ns - 1) / fs; % 载波幅度归一化为1 carrier cos(2 * pi * fc * t); % 2ASK调制乘法器法实现OOK ask_tx baseband .* carrier;这里我故意把载波幅度设为1。幅度取多大不影响误码率结论因为噪声功率会随信号功率一起换算但幅度为1会让后续观察波形时更清爽。如果你希望仿真结果跟理论公式里的A对应起来也可以定义A 1效果一样。乘法器法的代码就三行关键点在于baseband和carrier要完全等长。怎么确认等长baseband是N_bitsNs个点t一样是N_bitsNs个点只要这两个长度一致调制就一定没问题。我见过不少同学在这里把t定义成N_bits个点结果是MATLAB直接报维度不匹配。2.4 画波形验证调制是否正确% ------- 波形展示只画前N0_bits个码元 ------- figure; subplot(3,1,1); stem(bits(1:N0_bits), LineWidth, 1, MarkerSize, 3); title(原始比特流前100 bits); xlabel(比特序号); ylabel(幅度); axis([0 N0_bits -0.2 1.2]); subplot(3,1,2); plot(t(1:N0_bits*Ns), baseband(1:N0_bits*Ns), LineWidth, 1.5); title(单极性NRZ基带波形); xlabel(时间/s); ylabel(幅度); axis([0 N0_bits*Ns/fs -0.2 1.2]); subplot(3,1,3); plot(t(1:N0_bits*Ns), ask_tx(1:N0_bits*Ns), LineWidth, 1.2); title(2ASK调制后的频带信号); xlabel(时间/s); ylabel(幅度);画图这里有个细节很容易被忽略只画前N0_bits个码元而不是全部2000个。因为如果全部画出来波形密密麻麻挤在一起根本看不出OOK的“有载波/无载波”切换效果。我实测2000个码元全画时图几乎就是一团黑所以截取前100个码元观察就够了。3. 信道加噪与两种解调方案代码实现3.1 按Eb/N0给信号加高斯白噪声信道仿真的核心是加噪声但初学者最容易在这里翻车。很多人直接用awgn(x, snr, measured)但awgn默认参数是信噪比SNR而不是通信原理里常用的Eb/N0。要画出教科书式的误码率曲线需要先把Eb/N0换算成等效噪声功率再手动加高斯白噪声。% ------- 信道加高斯白噪声 ------- EbNo_dB 10; % 先以10dB为例 EbNo 10^(EbNo_dB/10); % 信号平均功率 Ps mean(ask_tx.^2); % 每比特平均能量 Eb Ps * Tb Ps / Rb Eb Ps / Rb; % 在线性白噪声采样模型下等效噪声单边谱密度 N0 Eb / EbNo N0 Eb / EbNo; % 噪声总功率白噪声在采样率fs范围内的总功率为 N0 * fs Pn N0 * fs; % 生成高斯白噪声 noise sqrt(Pn) * randn(size(ask_tx)); ask_rx ask_tx noise;这里要解释一下为什么噪声功率是N0 * fs。在离散时间仿真中我们把功率谱密度为N0的噪声通过采样率fs采样噪声的总功率在数值上等于N0 * fs可以理解为双边噪声谱密度N0/2在[-fs/2, fs/2]上积分。这个换算关系很多人容易搞混我在调试时也卡了很久。如果你图省事也可以直接写成noise sqrt(N0 * fs) * randn(size(ask_tx))和上面的代码等价。加完噪声后建议用figure; plot(t, ask_rx)看一眼噪声下的波形信噪比低的时候OOK包络会明显变模糊这也是后面解调性能变差的直观体现。3.2 相干解调乘法器加低通滤波加抽样判决相干解调的原理一句话“用本地载波乘回来再低通滤掉高频分量恢复基带包络”。本地载波必须和发送端载波同频同相这里我们假设理想载波同步直接用发送端的carrier做乘法。% ------- 相干解调 ------- % 1. 乘法器接收信号乘以本地同频同相载波 mix_coherent ask_rx .* carrier; % 2. 低通滤波这里用移动平均作为积分器窗口长度Ns window ones(1, Ns) / Ns; lp_coherent filter(window, 1, mix_coherent); % 3. 抽样判决取每个码元周期内的最后一个采样点 sample_index Ns : Ns : length(lp_coherent); sampled_coherent lp_coherent(sample_index); % 4. 判决门限取最大值的一半工程上简单有效 threshold_c max(sampled_coherent) / 2; bits_rx_c sampled_coherent threshold_c;为什么乘法器之后要用移动平均这个滤波器就是我们常说的“积分器”或“匹配滤波器”的简化版。它把每个码元周期内所有采样点等权相加后除以Ns等效于对每个码元做一次积分清洗。数学上矩形脉冲的匹配滤波器就是积分器所以这个移动平均在2ASK这种矩形包络下性能接近最优。用filter而不是conv是因为filter是流式滤波输出和输入等长conv会额外多出Ns-1个拖尾点后面抽样索引会乱。这一点我踩过坑当时用conv之后发现采样点的位置全偏了误码率怎么跑都不对后来换成filter才恢复正常。抽样点的选取也很关键。sample_index Ns:Ns:end的含义是每个码元的第Ns个采样点即码元末尾。为什么取末尾而不是开头或中间因为移动平均滤波器是因果的它在第k个码元的前半段还没把当前码元的数据收齐输出会混合上一个码元的残留。只有取到码元末尾甚至再往后偏一点窗口内才完整包含了当前码元的所有采样值。3.3 非相干解调整流加包络检波加抽样判决非相干解调不需要恢复载波实现上更简单代价是抗噪声性能比相干解调差一点。经典的做法是“整流加低通”提取包络。% ------- 非相干解调 ------- % 1. 全波整流取绝对值 mix_env abs(ask_rx); % 2. 低通滤波同样的移动平均窗口提取包络 lp_env filter(window, 1, mix_env); % 3. 抽样判决 sampled_env lp_env(sample_index); threshold_nc max(sampled_env) / 2; bits_rx_nc sampled_env threshold_nc;非相干解调的整流操作是包络检波的核心。整流后OOK信号的载波负半周被翻到正半周再经过低通滤波器就得到近似原基带信号的包络。你可以在MATLAB里画一下整流前后的波形abs(ask_rx)之后正半周不变负半周翻上去整个波形全是正的了再移动平均后就变得平滑这就是包络。非相干解调比相干解调简单但它有个固有弱点噪声会通过整流产生“整流噪声”导致即使信号完全断开比特0包络输出也会有非零的直流底噪。这个底噪在高信噪比时不明显但低信噪比时会把判决门限抬高误码率上升明显。4. 误码率统计与仿真结果对比4.1 单次仿真的误码率计算% ------- 误码率统计 ------- error_coherent sum(bits ~ bits_rx_c); error_noncoherent sum(bits ~ bits_rx_nc); ber_coherent_single error_coherent / N_bits; ber_noncoherent_single error_noncoherent / N_bits; fprintf(Eb/N0 %.1f dB\n, EbNo_dB); fprintf(相干解调误码率%.6f%d / %d\n, ber_coherent_single, error_coherent, N_bits); fprintf(非相干解调误码率%.6f%d / %d\n, ber_noncoherent_single, error_noncoherent, N_bits);误码率统计本身很简单sum(bits ~ bits_rx)就是错误比特数除以总数就是误码率。但这里有个工程细节2000个比特在高信噪比比如Eb/N0 14 dB下可能一个错误都没有误码率直接算出来是0。误码率为0画在对数坐标上会落到负无穷曲线画不出来。所以做BER扫描时要么增大仿真比特数要么给误码率加一个下限比如当误码率为0时记为1/N_bits表示“至少有一半比特错误概率的下界估计”。4.2 多信噪比扫描与理论曲线对比单次仿真只能得到一个点的误码率课程设计通常要求一条完整的BER曲线。这需要用循环扫描不同Eb/N0每个点都重新走一遍“调制—加噪—解调—判决—统计”全流程。% ------- BER曲线扫描 ------- EbNo_dB_list 0:1:14; ber_c zeros(size(EbNo_dB_list)); ber_nc zeros(size(EbNo_dB_list)); for k 1:length(EbNo_dB_list) EbNo_dB EbNo_dB_list(k); EbNo 10^(EbNo_dB / 10); % 产生随机比特每一轮都重新生成避免不同信噪比之间比特流完全相同 bits randi([0, 1], 1, N_bits); baseband repelem(bits, Ns); t (0 : N_bits * Ns - 1) / fs; carrier cos(2 * pi * fc * t); ask_tx baseband .* carrier; % 加噪 Ps mean(ask_tx.^2); Eb Ps / Rb; N0 Eb / EbNo; Pn N0 * fs; noise sqrt(Pn) * randn(size(ask_tx)); ask_rx ask_tx noise; % 相干解调 mix_coherent ask_rx .* carrier; lp_coherent filter(window, 1, mix_coherent); sampled_coherent lp_coherent(Ns : Ns : end); threshold_c max(sampled_coherent) / 2; bits_rx_c sampled_coherent threshold_c; % 非相干解调 mix_env abs(ask_rx); lp_env filter(window, 1, mix_env); sampled_env lp_env(Ns : Ns : end); threshold_nc max(sampled_env) / 2; bits_rx_nc sampled_env threshold_nc; % 误码率 ber_c(k) sum(bits ~ bits_rx_c) / N_bits; ber_nc(k) sum(bits ~ bits_rx_nc) / N_bits; end % ------- 理论曲线 ------- EbN0_linear 10.^(EbNo_dB_list / 10); % 相干2ASKOOKPe Q(sqrt(Eb/(2*N0))) ber_theory_c 0.5 * erfc(0.5 * sqrt(EbN0_linear)); % 非相干2ASKOOKPe 0.5 * exp(-Eb/(2*N0)) ber_theory_nc 0.5 * exp(-EbN0_linear / 2); % ------- 作图 ------- figure; semilogy(EbNo_dB_list, ber_c, bo-, LineWidth, 1.5, MarkerSize, 7); hold on; semilogy(EbNo_dB_list, ber_nc, rs-, LineWidth, 1.5, MarkerSize, 7); semilogy(EbNo_dB_list, ber_theory_c, b--, LineWidth, 1.2); semilogy(EbNo_dB_list, ber_theory_nc, r--, LineWidth, 1.2); grid on; xlabel(Eb/N0 (dB)); ylabel(误码率 BER); legend(相干解调仿真, 非相干解调仿真, 相干解调理论, 非相干解调理论, Location, southwest); title(2ASK调制解调误码率性能对比);跑完这段代码你会看到仿真点和理论曲线基本吻合相干解调在相同Eb/N0下明显优于非相干解调大约有1到2 dB的优势这正是通信原理教材里反复强调的结论。4.3 仿真结论从误码率曲线可以读出的三个结论也是课程设计报告里常用到的分析方向第一相干解调性能优于非相干解调。在误码率为10^-3量级时相干比非相干大约省2 dB左右的信噪比。原因是相干解调的本地载波携带了相位信息能更好地抑制噪声非相干解调在整流步骤引入了额外的噪声。第二两种解调方案的仿真曲线都与理论公式高度吻合验证了代码的正确性。如果仿出来的点偏离理论曲线太远比如差好几个数量级那几乎可以肯定是某个环节出了问题大概率是滤波、抽样或门限设置。第三信噪比越高误码率下降越快这是通信系统的基本规律。但注意在很高信噪比时仿真点可能出现“毛刺”那是统计不够导致的2000比特在低误码率时统计误差已经比较大所以扫描曲线时Eb/N0范围不要取得太高14 dB左右在2000比特下已经比较极限了。5. 调试中容易踩的坑和优化建议5.1 滤波器参数选不好导致性能大打折扣我最初调试这段代码时接收端用的是FIR低通滤波器fir1加filter但截止频率怎么选都很难跟理论曲线完全贴住。截止频率太高载波二次谐波滤不干净太低基带矩形脉冲被展宽引起码间串扰。后来我改成移动平均滤波器问题一下子简单了。移动平均本质是矩形窗积分器对矩形脉冲是最优的不需要调任何参数。你如果非要用FIR记住一个经验值低通截止频率设为1.2 * Rb到2 * Rb之间比较合适。但纯从“省事且正确”的角度我推荐直接用移动平均。另一个容易踩的坑是用conv做滤波。filter输出等长conv输出长度会多出窗长减一。如果后面抽样还用Ns:Ns:end那最后一个码元的抽样点会错位误码率统计自然不准。5.2 门限判决怎么设才稳我在代码里用的是“最大抽样值的一半”作为判决门限这在接收机已知训练序列或实时检测最大幅度的前提下是简单有效的。但实际工程里信道噪声可能导致个别抽样值异常大max会被拉高。更稳妥的做法是取发送信号的理论幅度中点或者统计抽样值的均值作为门限。给一个改进思路在发送端加一段已知导频序列接收端先用导频段的抽样值估计“1”电平的幅度再取一半做门限。这比用整个数据段的max更抗异常值。如果想让仿真曲线更贴近理论还有一个更简单的方法把判决门限固定为理论最优门限。在等概率0/1且加性高斯白噪声条件下OOK最优门限约为信号幅度的一半加噪声功率的修正项高信噪比下近似就是A/2。在仿真里如果你明确知道发送信号幅度直接把门限设成该值会比用max少一点统计波动不过两种都行不影响大结论。5.3 扩展建议这套代码还能往哪里改这套2ASK仿真框架可扩展的方向不少我列几个你课后可以练手的点改成2FSK载波换成cos(2*pi*fc1*t)和cos(2*pi*fc2*t)两个频率判决方式变成“比较两路包络或相干输出”代码改动量不大但能加深对非相干检测的理解。改成2PSK把单极性NRZ0和1改成双极性NRZ-1和1这时的相干解调误码率公式会变成0.5*erfc(sqrt(Eb/N0))和2ASK有3 dB差距你可以用仿真验证这个经典结论。加同步误差手动给本地载波引入相位偏差比如10度、30度观察相干解调性能退化可以直观理解载波同步的重要性。换成实际带通滤波器加包络检波用fir1设计带通滤波器中心频率fc带宽2Rb再接整流和低通模拟真实超外差接收机结构。最后再分享一个我在代码调试过程中的细节波形观察和误码率统计要分开做。跑BER曲线时不要画波形图不然100个循环能卡到怀疑人生。只有单独需要展示波形时才画图并把figure放在循环外面。代码性能上MATLAB不是为大规模循环优化的所以N_bits和Eb/N0扫描点数都不要取得太大2000比特加15个信噪比点在普通笔记本上几秒钟就跑完了这个规模完全够用。本文还有配套的精品资源点击获取
网站建设高端定制企业官网