ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

频分复用通信模型Matlab仿真:bandpass.m滤波与相干解调实现

频分复用通信模型Matlab仿真:bandpass.m滤波与相干解调实现 简介频分复用通信模型MATLAB仿真研究面向通信工程、电子信息类学生及需要完成课程设计或毕业设计的开发者用于理解FDM系统中多路信号在频域上如何通过不同载波进行频谱搬移、叠加合成为一路复合信号再经接收端带通滤波与解调还原出各路基带信号。压缩包共4个文件其中三个m脚本分别承担信号生成、带通滤波器设计与系统主流程控制一个mat数据文件提供实测音频样本资源整体约22.11MB结构简洁便于逐段阅读与二次开发。目前已有1503人下载学习。通过运行仿真能够直观看到各通道频谱在发送端的错位排列、接收端滤波后的波形变化并学习滤波器截止频率、滚降特性等参数对解复用效果的影响音频素材还能用于对比原始与还原音质的差异从而完整掌握FDM收发端设计与调试的关键步骤。1. 频分复用通信模型仿真先看懂bandpass.m再谈复用做通信原理课程设计时我见过太多人把三路信号各自乘上载波后直接相加解调端却一片噪声。问题几乎不在乘法器而在bandpass.m那几行滤波参数上。这份频分复用通信模型matlab仿真研究资源里真正核心的是audio.mat、getaudio.m、code.m、bandpass.m四个文件的配合方式getaudio.m负责读入语音素材code.m做频分复用调制bandpass.m承担解复用端的通道分离。适合需要手写调制解调底层逻辑、而不是只拖Simulink模块的Matlab通信仿真场景也适合做FDM课程设计或毕业设计的人用来对照参数。下面按复用端、解复用端、完整链路和验证技巧的顺序拆开讲。2. 复用端参数分配audio.mat组织与code.m载波调制2.1 getaudio.m与audio.mat约定先统一采样率再谈频谱搬移audio.mat里通常是一个结构体或矩阵常见做法是保存成包含原始语音数据和采样率的字段比如audio_data(1).samples、audio_data(1).fs。getaudio.m的作用是从这个文件里把三路语音取出来按统一格式对齐。有一个细节经常被忽略录音设备的采样率往往是8kHz或16kHz而FDM想要把多路基带信号搬到不同频段必须先把基带信号上采样到足够高的系统采样率否则载波频率稍微抬高一点就会触发奈奎斯特混叠。我一般在getaudio.m里这样组织读取逻辑function [x1, x2, x3, fs_base] getaudio() % 从 audio.mat 中读取三路语音 load(audio.mat, audio_data); x1 audio_data(1).samples; x2 audio_data(2).samples; x3 audio_data(3).samples; fs_base audio_data(1).fs; % 原始采样率一般为 8000 Hz end这段代码的核心是把文件内容和仿真主流程解耦。audio_data的字段名不一定是samples建议先用whos -file audio.mat看一下实际变量名再写赋值。fs_base这一句值得留意后面所有上采样和载波频率设计都以它为基准。如果发现三路语音采样率不一致必须在读取后统一重采样比如用resample(x2, fs_base, audio_data(2).fs)让三路落到同一个时间轴上。参数说明audio_data(1).fs第一路语音的原始采样率决定后续内插倍数。若audio.mat里是普通数组而非结构体建议在getaudio.m里直接按列切分不要在主脚本里做。因为主脚本的数据维数变化会直接影响载波生成长度一旦某个文件里多存了一行后面所有cos函数都会维度不匹配。2.2 频带划分载波频率与保护间隔怎么定基带语音的有效带宽按4kHz算三路信号要互不重叠最简单的方案是让每路占据8kHz的频谱槽位其中4kHz是信号本身4kHz是保护间隔。保护间隔留得太小bandpass.m的带通滤波器过渡带很难做阶数要拉得非常高留得太大系统采样率又得翻倍。结合常见音频采样率我按下面这组参数来分配项目数值说明基带采样率 fs_base8000 Hz原始语音采样率有效带宽 B4000 Hz4kHz语音上限载波频率 f116000 Hz第一路中心频率载波频率 f224000 Hz第二路中心频率载波频率 f332000 Hz第三路中心频率保护间隔4000 Hz与相邻路相隔4kHz系统采样率 fs_sim128000 Hz覆盖三路搬移后的最高频率这里最高一路信号频率是32k4k36kHz理论上fs_sim取72kHz就够。但实际FIR带通滤波器需要过渡带空间同时要保证载波生成时cos参数有足够的采样点所以128kHz是我常用的值。如果你后续要用高阶滤波器这个采样率能明显缓解数值稳定性问题。2.3 code.m调制过程乘余弦载波的隐藏细节code.m的简化实现如下注意我把fs_base也作为参数传进来避免在函数内部硬编码8000function s code(x1, x2, x3, fs_base, fs_sim, f_carrier) % 三路语音频分复用调制 N_base max([length(x1), length(x2), length(x3)]); t_base (0:N_base-1) / fs_base; N_sim fix(N_base * fs_sim / fs_base); t (0:N_sim-1) / fs_sim; % 内插到系统采样率 x1_up interp1(t_base, x1, t, spline, 0); x2_up interp1(t_base, x2, t, spline, 0); x3_up interp1(t_base, x3, t, spline, 0); % 乘载波叠加 s x1_up .* cos(2 * pi * f_carrier(1) * t) ... x2_up .* cos(2 * pi * f_carrier(2) * t) ... x3_up .* cos(2 * pi * f_carrier(3) * t); end这里最容易踩坑的不是乘法而是t的长度。如果三路原始长度不一样直接用length(x1)生成t后面x2_up就会维度不匹配。所以我先对所有信号取max长度保证每路都在同一个时间轴描述上。N_sim用fix而不是round是为了让长度严格同步避免滤波器内部出现“差一个点”的索引错误。另一个隐藏细节是内插方式。interp1的spline会引起边缘过冲如果输入是语音波形过冲可能造成载波幅度被推高。更稳的做法是直接用resample(x, fs_sim, fs_base)做多相滤波内插抗混叠特性更可控。上面代码用interp1是为了让逻辑更直白实际项目我更倾向resample。调制本身并不复杂三路分别乘cos(2pif*t)后累加得到的s就是频分复用信号。你可以用fft(s)看一眼频谱理想情况是三个频带像小山包分别位于16k、24k、32k附近。如果看到山包之间有鼓包先怀疑内插后出现了镜像频谱而不是载波设计出错。3. bandpass.m解复用端实现从通道分离到相干解调3.1 bandpass.m滤波器设计FIR阶数、通带与过渡带解复用端的核心是把复合信号里某一频段单独取出来。bandpass.m本质上就是一个带通滤波器封装。下面给出可用的实现function y bandpass(x, fs, f_center, bw, order) % 基于fir1的带通滤波器 f_low f_center - bw / 2; f_high f_center bw / 2; if mod(order, 2) 1 order order 1; % fir1要求阶数为偶数 end b fir1(order, [f_low, f_high] / (fs / 2), bandpass); y filter(b, 1, x); end参数说明f_center目标通道载波频率比如16000Hz。bw通道带宽建议设为8000Hz也就是把保护间隔也包含进来这样滤波后的信号仍保留完整4kHz语音只是邻道泄漏会略高。order滤波器阶数阶数越高过渡带越窄但延迟越大200阶在128kHz采样率下已经能提供较陡的滚降。关于通道带宽这里有个取舍。bw取得和语音带宽一样4kHz滤波器通带边缘会顶着语音上边界语音高频分量会被削掉bw取8kHz邻道信号会多进来一点要靠低通滤波再处理。我测试下来语音场景bw取8k明显更自然因为听感不是看隔离度指标而是看语音有没有被滤波削边。滤波器类型适用场景注意事项fir1等纹波带通需要线性相位、通带平坦order必须为偶数控制阶数防延迟butter带通阶数相同时计算更稳、但相位非线性对相位不敏感的场景可用filtfilt离线仿真去相位失真不能用于实时流式处理3.2 相干解调与低通滤波为什么乘法后不能立刻恢复取出一路带通信号后要把它从载波附近搬回基带。相干解调就是在时域乘以与发送端同频同相的载波。以第一路为例% 从复合信号s中取出第一路 x1_bp bandpass(s, fs_sim, 16000, 8000, 200); % 乘同频载波进行频谱搬移 t (0:length(s)-1) / fs_sim; x1_rec_base 2 * x1_bp .* cos(2 * pi * 16000 * t); % 低通滤波截止频率留一点余量 b_lp fir1(256, 4500 / (fs_sim / 2), low); x1_rec filter(b_lp, 1, x1_rec_base);说明乘2是为了补偿解调时产生的1/2幅度损耗。如果不乘2后面波形幅度会比原信号低6dB听感变弱但也算能工作这里建议保留。低通滤波截止频率不能设在4kHz整因为滤波器有过渡带取4.5kHz更适合语音在4k附近还有少量能量。乘法器输出中包含目标通道基带也包含高频分量和邻道泄漏。低通截止频率既决定语音质量也决定邻道隔离度。相干解调对载波同步极其敏感。如果解调载波频率偏移十几赫兹语音听起来会像“含水的声音”。在这个仿真里发送端和接收端用的是同一个载波生成器所以相位天然同步。真正需要警惕的是cos函数输入里t的小数误差长时间信号运算后cos参数会丢失精度导致解调后出现缓慢的幅度漂移。3.3 仿真发散与频谱泄漏先查阶数再查数据长度Matlab仿真里报“发散”一般不是数值无穷而是滤波输出不断膨胀或波形断裂。常见原因有两种。第一种是filter的输入是整段复合信号但信号前后沿有突变滤波器卷积会在开头产生瞬态响应幅度有时会超过正常值。解法是去掉每段输出的前order个采样点或者用filtfilt替代filter做零相位处理。第二种是数据长度不匹配上采样后N_sim用了fix取整导致t向量和x向量长度差一。解决方法是统一用length(t)来截断所有信号或者在bandpass.m入口检查length(x)和fs的关系。频谱泄漏则可以用零填充观察对y做N*4点的FFT频谱峰值会变得更精细便于确认每一路频谱的边界有没有压到相邻通道。4. 完整链路复现与参数调优4.1 把四个文件串成一条可运行链路下面的主脚本把getaudio.m、code.m、bandpass.m串成一个完整的FDM仿真% main_fdm.m - 三路语音频分复用仿真 [x1, x2, x3, fs_base] getaudio(); % 读取audio.mat fs_sim 128000; f_carrier [16000, 24000, 32000]; % 发送端调制 s code(x1, x2, x3, fs_base, fs_sim, f_carrier); % 接收端解复用通道带宽8k bw 8000; x1_bp bandpass(s, fs_sim, f_carrier(1), bw, 200); x2_bp bandpass(s, fs_sim, f_carrier(2), bw, 200); x3_bp bandpass(s, fs_sim, f_carrier(3), bw, 200); % 相干解调第一路 t (0:length(s)-1) / fs_sim; x1_demod filter(fir1(128, 4500/(fs_sim/2)), 1, 2*x1_bp .* cos(2*pi*f_carrier(1)*t)); % 第二、三路同样处理这里省略这个脚本的优点是每个模块独立出问题可以直接用频谱对比定位。运行前建议先用fvtool(b,1,fs_sim)查看bandpass.m生成的滤波器幅频响应确认通带位置正确再往下跑。很多情况下滤波器设计错了后面解调再复杂也收不回来。4.2 参数表一张表做完整调优对照参数位置参数名建议值调优方向载波f_carrier16k/24k/32k相邻间隔不宜小于8k系统采样率fs_sim128k载波频率越接近fs/2此值需要更高通道带宽bw8k减小可提高隔离度但语音会被削边FIR带通阶数order200阶数越大过渡带越窄但延迟越大低通截止f_lp4.5k保持语音自然度可微调参数调整有个优先顺序。如果串话严重先调带通阶数从128加到256观察是否改善如果带宽外噪声大再检查bw是否过大如果语音发闷则把低通截止左移右移。不要一上来就同时动三个参数否则根本不知道是哪个改动生效。4.3 和Simulink仿真模型的区别与互补很多人会把这套脚本改成Simulink仿真模型用Spectrum Analyzer看频谱用Analog Filter Design搭带通。Simulink的好处是模块化清晰坏处是采样率变化被封装隐藏一旦出现混叠不好追踪。脚本仿真让你能明确看见每个中间变量是几维、长度多少排查仿真发散更快。我的建议是先用脚本方式跑通再把关键模块复制成Simulink子模块这样即使后续要扩展成OFDM或其它多载波系统基础逻辑也是清楚的。5. 用chirp信号和零填充FFT量化邻道隔离度做完三路语音复用后最常遇到的问题是解调出来的语音能听但不知道邻道串扰到底多大。与其靠耳朵不如用一个固定流程量化隔离度。把其中一路输入换成线性调频信号chirp其它路保持静音在解调端对目标通道做FFT% 对解调后的目标通道信号做零填充FFT评估频谱泄漏和隔离度 N length(x1_rec); X fft(x1_rec, N * 4); f_axis (0:N*4-1) * fs_sim / (N*4); % 目标频带定义为语音有效范围 target_band (f_axis 50 f_axis 4000); leak_band (f_axis 4000 f_axis 8000); isolation_db 20 * log10(mean(abs(X(target_band))) / mean(abs(X(leak_band))));这段代码的思路是把目标频带内的平均能量和相邻保护频带内的平均能量作比得到隔离度。隔离度高于40dB基本听不见串话30dB左右还能接受。如果isolation_db低于25dB优先回头调bandpass.m的order把200改成300观察隔离度是否明显提升。另一个技巧是检查chirp解调后的瞬时频率。chirp从500Hz扫到3500Hz解调端用spectrogram观察应该看到一条随输入频率线性变化的谱线。如果谱线中间有断裂说明带通滤波器在该频点出现凹陷如果谱线在边界附近变粗说明低通滤波器截到了语音上边带。最后一个小操作在bandpass.m输出后面加一行filtfilt替换filter。零相位滤波虽然不能改善隔离度但能消除滤波器相位畸变带来的波形失真让后续频谱分析的峰值更尖锐。调试隔离度时用filter还是filtfilt都能定位问题确认参数后正式仿真建议保留filtfilt因为它的群延迟为零做相干解调时不会引入符号定时偏移。本文还有配套的精品资源点击获取
返回列表