简介:基于 Matlab/Simulink 的数字基带传输系统仿真文档,适合通信工程和电子信息类专业学生、课程设计者及科研人员,用以掌握数字基带信号的编码、传输与误码性能分析。文档从数字基带传输系统的基本概念切入,详细说明 AMI、HDB3、双相码、CMI 等码型的原理与适用场景,并基于 Simulink 的可视化建模环境完成发送/接收滤波器、时钟恢复、位定时提取等核心模块的设计与联调。通过仿真可得到基带波形和误码率曲线,并与理想双极性系统进行对比,结果基本吻合,验证了仿真模型的正确性。整套资源打包为单个 docx 文档,大小约 786KB,结构涵盖系统概述、仿真模型构造、实验设计与结果分析等章节,便于按步骤阅读和复用。已有 266 人学习,既可以作为通信原理课程设计、数字通信仿真实习的参考,也可供教师备课和科研人员快速搭建数字基带仿真模型使用。
1. 数字基带传输系统仿真:从波形到误码率的完整闭环
数字基带传输系统是所有通信系统仿真里最容易被低估的一环。很多人直接跳过基带,一上来就搭 QPSK、OFDM 的高频链路,结果接收端波形发散、误码率曲线对不上理论值,回头排查才发现是基带的采样率、滤波器滚降系数和抽样判决时刻没有对齐。用 MATLAB/Simulink 做数字基带传输系统仿真,本质是把「信源 → 码型变换 → 发送滤波 → 信道 → 接收滤波 → 抽样判决 → 误码统计」这条链路在离散时间域里复现一遍,难点不在 Simulink 操作,而在每个模块的采样时间、延迟和数据类型是否一致。本文适合两类人:一类是刚接触通信仿真、想用 Simulink 把教材上的基带理论跑通的学生,另一类是做 FPGA 或软件无线电、需要先验证基带算法再落硬件的工程师。前者能照着步骤搭出可运行的模型,后者能直接复用里面的参数配置和脚本验证方法。
2. 数字基带传输的理论边界与噪声模型选择
2.1 NRZ/RZ 波形与功率谱密度的关系
数字基带信号最常见的两种码型是单极性 NRZ 和双极性 NRZ。单极性 NRZ 在 "1" 时输出高电平、"0" 时输出 0,频谱中包含离散的直流分量和时钟分量;双极性 NRZ 在 "1" 时输出正电平、"0" 时输出负电平,频谱中没有离散谱线,功率集中在低频段。这个差异直接决定了 Simulink 模型里信道的直流偏置处理方式——单极性 NRZ 经过隔直电容后会引入基线漂移,而双极性 NRZ 不会。
在 Simulink 里生成 NRZ 波形不能用简单的 Step 信号拼接,而是要基于随机整数源和采样保持。常见做法是:用 Bernoulli Binary Generator 产生 0/1 序列,再经过一个 Gain 和 Bias 的组合把 0/1 映射成 -1/+1。映射关系可以用一个查表模块替代,但 Gain+Bias 更直观,参数也更容易回读。
% 在 MATLAB 脚本中预计算双极性 NRZ 的功率谱,用于和 Simulink 结果对比 fs = 1000; % 采样率 Hz Tb = 0.01; % 码元宽度 s t = 0:1/fs:Tb*100; % 100 个码元的时长 data = randi([0 1], 1, 100); nrz = 2*data - 1; % 双极性映射 nrz_up = reshape(repmat(nrz, length(t)/100, 1), [], 1)'; [pxx, f] = pwelch(nrz_up, hamming(256), 128, 512, fs); plot(f, 10*log10(pxx)); grid on; xlabel('频率 Hz'); ylabel('功率谱密度 dB/Hz'); title('双极性 NRZ 功率谱');这段代码里最关键的是repmat之后的reshape方向。如果维度写反,得到的是按列填充的矩阵转置后的一维数组,波形会在码元切换点出现毛刺。pwelch的窗长取 256、重叠 128、FFT 点数 512,是通信仿真里比较常规的一组参数,窗长太短频谱会过度平滑,滚降特性看不出来。
2.2 码间串扰与升余弦滚降滤波
数字基带传输系统仿真的核心矛盾是带宽受限带来的码间串扰。理论上奈奎斯特第一准则告诉我们可以用带宽为 Rb/2 的理想低通滤波器做到无码间串扰,但理想低通的冲击响应衰减慢,定时抖动下会迅速恶化。工程上普遍选择升余弦滚降滤波器,用多余的带宽换取对抽样时刻偏差的容忍。
升余弦滤波器在 Simulink 里有两个层面的实现:一是用 Raised Cosine Transmit Filter 模块做脉冲成形,二是用 MATLAB 里的rcosdesign函数离线生成系数再导入 FIR 模块。我的建议是优先用rcosdesign配合 Discrete FIR Filter,因为这样你能直接控制滤波器阶数和量化位宽,下游做 FPGA 定点仿真时可以直接复用系数文件。
span = 10; % 滤波器覆盖的码元个数 rolloff = 0.5; % 滚降系数,越大带宽越宽,波形越平缓 sps = 8; % 每码元采样点数,必须是整数且大于 2 h = rcosdesign(rolloff, span, sps, 'sqrt'); % 根升余弦,收发各用一半 fprintf('滤波器长度 = %d, 归一化带宽 = %.2f Hz\n', length(h), (1+rolloff)/2);这里的'sqrt'参数值得强调。如果发送端和接收端各自用一个根升余弦滤波器,级联后的响应等效为一个完整的升余弦滤波器,总带宽正好是 (1+rolloff)/2 倍码元速率。很多人直接在链路里放两个完整升余弦滤波器,结果是等效带宽更窄,眼图张开度变小,误码率比理论值偏高。滚降系数取 0.35~0.5 在仿真里最稳妥,取 0 时滤波器长度不变但时域拖尾严重,抽样位置偏离一点就会出现明显 ISI。
2.3 AWGN 信道与信噪比定义
Simulink 的 AWGN Channel 模块里有两种 SNR 定义方式:按 Eb/No 或按 Es/No。对数字基带传输系统,误码率理论曲线通常是关于 Eb/No 的函数,双极性 NRZ 在匹配滤波下的误码率是 Q(sqrt(2*Eb/No))。用 AWGN Channel 模块时,要把输入端口的信号功率先测量出来,再设置 SNR(dB),因为模块默认按输入信号功率计算噪声方差。
测量信号功率最简单的方法是在发送滤波器输出后接一个 Display 模块,同时将仿真时间设成整数个码元的整数倍,让平均值窗口恰好覆盖完整的码元周期。信号功率测量不准是 BER 曲线偏移的主要原因,比滤波器参数错误更难排查。
% 计算给定 Eb/No 下双极性 NRZ 的理论误码率 EbNo_dB = 0:2:12; EbNo_lin = 10.^(EbNo_dB/10); Pe = erfc(sqrt(EbNo_lin)) / 2; % 注意不乘 2,双极性 PAM 的公式 semilogy(EbNo_dB, Pe, 'b-o'); grid on; xlabel('Eb/No (dB)'); ylabel('误码率'); title('双极性 NRZ 理论误码率');这里用erfc而不是qfunc是因为erfc在 MATLAB 基础工具箱里就有,而qfunc需要 Communications Toolbox。注意公式里没有乘以 2,很多教材里写成 P = Q(sqrt(2Eb/N0)),对应的是双极性信号在匹配滤波后的误码率,乘以 2 是单极性或 OOK 的近似。
3. 用 Simulink 搭建数字基带传输链路的最小可运行模型
3.1 信号源与并串转换的设置
打开 Simulink 空白模型,从 Communications Toolbox 里拖入 Bernoulli Binary Generator。核心参数有三个:Probability of a zero 默认 0.5,Sample time 设为码元周期的整数倍,Samples per frame 决定一次输出多少个码元。这里有一个容易踩的坑:如果把 Sample time 直接设为码元周期 Tb,那么后面的脉冲成形滤波器必须设置为每码元多个采样点,否则滤波器输出速率不足,无法生成平滑的基带波形。
常用做法是让信号源按每个码元输出一个样本,然后在发送滤波器里通过插值实现上采样。Raised Cosine Transmit Filter 模块的 Input samples per symbol 设为 1,Output samples per symbol 设为 8,滤波器内部会自动完成插值。如果你想让信号源直接输出每码元 8 个样本,也不是不行,但要额外在滤波器前加一个 Downsample 模块,处理不好会引入相位偏置。块与块之间的采样率关系可以通过菜单 Display → Signals & Ports 里的 Sample Time Legend 查看,红色表示连续信号,彩色数字表示离散采样率。
并串转换在纯基带仿真里其实可以省略。只有当你要把多路并行数据变成一路串行码流时,才用 Parallel to Serial Converter 模块,它的 Block size 参数要和上游帧长度匹配。帧长度的不匹配会导致数据错位,现象很隐蔽——眼图正常,星座图正常,但误码率一直停留在 0.5 左右。遇到这种情况先检查每条信号线的 Sample Time 标注,再看每个模块的帧长度。
3.2 升余弦发送滤波器与抽样判决模块配置
发送端用 Raised Cosine Transmit Filter,关键参数是 Filter span in symbols(建议 10)、Roll-off factor(建议 0.35)、Output samples per symbol(建议 8)。滤波器系数量化位宽默认是全精度,如果后续要接定点链路,可以在 Filter coefficients 里勾选 Inherit via internal rule,但这会改变滤波器响应,BER 曲线会有约 0.1 dB 的损失,属于正常现象。
接收端用一个相匹配的 Raised Cosine Receive Filter,参数必须与发送端完全一致。两个滤波器级联后,眼图在最佳抽样点附近会非常清晰。抽样判决用 General Block Deinterleaver 不一定合适,更直接的方式是 Manual Switch 配合 Triggered Subsystem,但最稳定的方案是直接用 Error Rate Calculation 模块内部自带的延迟对齐功能,它内部会做数据对齐,不需要手动找最佳抽样时刻。
我通常会在接收滤波器后加一个 Scope 看波形,并且把 Scope 的 Sampling 模式设为 Decimation,值设成 8,这样 Scope 上每 8 个点显示一个点,等效于在码元中心抽样。如果 Decimation 值不对,Scope 上看到的就是一个密实的波形带,点开之后也看不出眼图的张开度。
3.3 用误码率统计模块验证理论曲线
链路最后接 Error Rate Calculation 模块,Rx 端口接判决后的数据,Tx 端口接延迟对齐后的原始数据。模块内部会输出三个量:误码率、错误比特数、总比特数。关键参数是 Receive delay,这个值必须等于发送滤波器群延迟和接收滤波器群延迟之和,单位是码元周期还是样本周期取决于模块文档。实际调试时不要手工数延迟,用 Error Rate Calculation 模块自带的自适应延迟即可,勾选 Output data 里的 BER 端口,然后在 MATLAB 脚本里循环设置 SNR 值跑仿真。
% 批跑 BER 曲线的核心循环代码 EbNo_dB = 0:2:12; ber_sim = zeros(size(EbNo_dB)); for k = 1:length(EbNo_dB) set_param('baseband_model/AWGN Channel', 'EbNo', num2str(EbNo_dB(k))); out = sim('baseband_model', 'StopTime', '1e5'); ber_sim(k) = out.BER(end); % 取最后一次统计结果 end semilogy(EbNo_dB, ber_sim, 'r-*'); hold on; % 叠加理论曲线,代码见 2.3 节set_param里的模块路径要和你模型里的名字完全一致,包括空格大小写。仿真停止时间设成 1e5 个码元,BER 在 1e-3 量级时大约有 100 个错误比特,统计波动已经小于 10%,如果只跑到 1e4 码元,BER 曲线会上下跳动,误以为是信道模型问题,其实只是样本量不足。
4. 仿真步长、缓冲区与随机种子:让 BER 曲线稳定收敛
4.1 采样时间与传输速率的整数倍约束
数字基带传输系统的每个离散模块都有自己的采样时间,Simulink 在仿真开始前不会强制你统一,但运行时会产生速率转换警告。最基础也是最重要的规则是:所有码元速率必须是整数关系。信号源 Sample time = 1,滤波器 Input samples per symbol = 1,Output samples per symbol = 8,此时滤波器的输出采样时间是 1/8。后面 AWGN 模块和误码率模块的采样时间必须是 1/8,不能设置成 1/7 或 0.12 这种非整数。
在离散模型中设置离散时间步长时,建议在 Configuration Parameters → Solver 里把 Type 设为 Fixed-step,Fixed-step size 设为 auto,Solver 选 discrete (no continuous states)。很多用户直接把 Solver 留默认的变步长 ode45,模型里没有连续状态时它也能跑,但缓冲区管理和 Scope 刷新会不稳定,BER 统计模块可能漏记数据。固定步长配合离散求解器是通信仿真的标准配置,可以避免仿真发散这类问题。
仿真步长与滤波器采样周期不匹配时,滤波器输出的波形会出现锯齿状抖动。排查方法是在接收滤波器的输出线上用 Probe 模块读取采样时间,如果显示的值和滤波器 Output samples per symbol 设置不符,说明有隐式的 Zero-Order Hold 或 Rate Transition 插入了链路。Rate Transition 本身不坏,但它的 Output port initial conditions 可能导致前几个码元数据损坏,影响 BER 统计。
4.2 显示缓冲区与信号属性检查
Scope 模块的默认缓冲是 5000 个点,如果你查看的信号采样率是码元速率的 8 倍,一个屏只能显示 625 个码元,而 BER 统计需要的数据量是 1e5 量级。两种解法:一是把 Scope 的 History 标签里的 Limit data points to last 改为 Unlimited,二是用 To Workspace 模块把波形导出,在 MATLAB 里用plot查看。
用 To Workspace 时有几个参数要小心。Variable name 里不能带点号,Save format 建议选 Timeseries,这样输出对象的Time属性里能拿到每个样本的绝对时间,方便后续计算延迟。如果选 Array,Simulink 会直接把采样时间列去掉,你还要额外记录步长,很麻烦。
% 读取 To Workspace 导出的接收波形,并手动抽取最佳抽样点 rx = out.rx_signal.Data; % TimeSeries 数据 t = out.rx_signal.Time; Tb = 1; % 码元周期,和模型设置一致 sps = 8; % 每码元采样点数 n_symbols = floor(length(rx) / sps) - 20; % 去掉头部暂态 idx = (10*sps+1) : sps : (10+n_symbols)*sps; % 从第 10 个码元起抽 sampled = rx(idx); scatter(1:n_symbols, sampled, 5, 'filled'); grid on;头部暂态是必须去掉的。滤波器群延迟大约为 span/2 = 5 个码元,前 5 个码元的输出是滤波器卷积窗口未填满时的部分响应,直接参与 BER 统计会引入系统性的偏差。这里的10*sps+1多给了 5 个码元的裕量,数据充足时建议直接扔掉前 20 个码元。
4.3 随机种子与蒙特卡洛重复运行
AWGN Channel 模块和 Bernoulli Binary Generator 模块都有随机种子参数。默认情况下每次仿真生成的噪声和信源码都不同,导致你在调参时无法判断效果改善是来自参数修改还是随机波动。固定种子的做法是:在 Bernoulli Binary Generator 的 Source of initial seed 里选 Parameter,输入一个固定值,比如 37;AWGN Channel 的 Random seed 也设成固定值。这样每次跑同样的参数组合,输出 BIT EXACT 一致。
固定种子之后,要评估一条 BER 曲线的稳定性,合理做法是保持信噪比不变、只改变噪声种子重复跑 10 次,把错误比特数的均值和方差记录下来。错误比特数近似服从泊松分布,方差约等于均值,因此当错误比特数大于 100 时,相对误差小于 10%,可以接受。种子的选取只需要保证不同 Eb/No 点之间不重复,不要用相同的噪声种子跑整条 BER 曲线,否则曲线会在同一 Eb/No 处出现关联性跳变。
5. 眼图、星座图与 MATLAB 脚本联合验证
5.1 Eye Diagram Scope 的触发与余晖设置
Simulink 里没有单独的叫 Eye Diagram 的模块,正确名称是 Communications Toolbox 里的 Eye Diagram Scope。双击打开后,在 Display 选项卡里设置 Samples per symbol 为 8,Symbols per trace 为 2,Traces 为 300。余晖时间设置很关键:余晖太短,眼图只显示最近几十个码元,轮廓模糊;余晖太长,显示刷新非常慢,调参数时反馈延迟大到不可接受。经验值是设成 2~3 秒。
触发位置决定眼图的水平对齐质量。Eye Diagram Scope 默认用第一个输入的上升沿作为触发,如果接收滤波器的输出带有直流偏置,触发点会抖动。要在 Display 菜单里把 Trigger offset 设成最佳抽样时刻对应的相对位置,对根升余弦滤波器加匹配滤波器的情况,这个值通常在码元周期的中点附近。
% 用离线方式画出眼图,效果与 Scope 等价且可保存 rx = out.rx_signal.Data; sps = 8; len = floor(length(rx)/sps) * sps; rx = rx(1:len); rx_mat = reshape(rx, sps, []); % 每个码元画一条线,绘制多个码元叠加 n_traces = size(rx_mat, 2) - 1; tt = (0:sps-1) / sps; figure; hold on; for k = 1:200 % 只画 200 个码元,避免线条过密 plot(tt, rx_mat(:, k), 'b-', 'LineWidth', 0.5); end xlabel('码元周期内相对时间'); ylabel('幅度'); title(sprintf('接收信号眼图 (滚降系数 %.2f)', 0.35));每次画 200 条线是为了让眼睛能分辨出轨迹密度。如果一次性画出几千条线,中间轨迹会叠加成一片纯黑色,看不出眼图的张开度。另外注意要把rx_mat的列数不足 200 的尾部裁掉,避免plot变长不齐。
5.2 从 Simulink 导出数据到 MATLAB 做 BER 拟合
BER 曲线跑完之后,除了和理论公式对比,还可以做一件事:用最小二乘拟合验证仿真结果的斜率是否与理论一致。理论误码率在误码率较小时近似指数衰减,把 BER 取对数后对 Eb/No 做线性回归,斜率接近 -1 时说明模型正确,斜率偏差超过 20% 时说明卷积码或滤波器参数有误。
EbNo_dB = 0:2:12; ber_sim = [0.18, 0.08, 0.025, 0.006, 0.001, 0.0002, 0.00003]; % 示例数据 % 取误码率大于 1e-5 的点做线性拟合 idx = ber_sim > 1e-5; p = polyfit(EbNo_dB(idx), log10(ber_sim(idx)), 1); fprintf('斜率 = %.2f dB/decade\n', p(1));有两点要提醒。第一,拟合时要去掉 Eb/No 过低的数据点,0 dB 附近理论曲线不在指数衰减区,拟合斜率虚高。第二,BER 仿真数据里若有 0 值,比如高信噪比时 1e5 个码元一个误码都没出现,log10 会给出 -Inf,直接把这类点剔除,不要让它参与拟合。纯基带仿真做到这一步已经具备完整的验证闭环:波形、眼图、BER 曲线三向印证,接下来无论是换调制方式、加均衡器,还是改成定点模型做 FPGA 实现,这个模型都可以作为基准链路继续扩展。最后一件事是养成保存时把随机种子和仿真步长写进模型描述的习惯,Simulink 模型的回调函数里加一行disp(['Eb/No = ', num2str(EbNo)]),这样回读模型时你能立刻知道这组仿真是什么条件下产生的。
本文还有配套的精品资源,点击获取