
简介这套Matlab代码面向通信专业学生、信号处理工程师与算法测试人员实现了无线电信号中的AGC自动增益控制算法。AGC能够根据接收信号强弱自动调整前级增益输入信号幅度增大时降低增益防过载信号较弱时提升增益改善信噪比从而保证输出幅度稳定。为便于理解算法工作过程包内源码完整还原了信号生成、功率测量、增益计算、控制更新和结果绘制等环节信号生成模块可模拟不同调制方式与强度功率测量模块通过窗口化平均估计瞬时功率增益控制模块根据功率与参考门限动态调节步长并通过循环迭代实现连续跟踪同时绘制仿真前后对比图。压缩包共15个文件含9个m脚本/函数和6个mat信号样本文件整体大小8.53MB模块划分清晰可读性强此外还可通过调整参考功率、环路带宽等参数控制响应速度抑制振荡与过冲为实际硬件实现提供参考。已有458人学习下载适合希望快速掌握AGC原理、进行通信算法仿真或二次开发的读者使用。1. 无线电信号里的 AGC 是一台自动变增益的机器第一次拿到老式的短波接收机拧音量旋钮时会发现一个奇怪现象同一个频率上话音忽大忽小噪声却始终稳定在一个水平上。这不是扬声器的问题而是前端电路里有一级自动增益控制AGC在起作用——它不停测量进入中频的信号强度再把可变增益放大器的倍数反过来调整让输出功率基本恒定。到了软件无线电时代这个环路被搬进了 Matlab 代码里成了接收机信号处理链上最容易被忽略、却又直接决定解调质量的环节。AGC 要解决的麻烦很具体天线口的信号动态范围可以超过 100 dB而 A/D 转换器和后面的解调算法只能舒服地工作在某个窄带范围内。信号太弱量化噪声把符号吞掉信号太强削顶失真引入一堆杂散。自动增益控制就是拿一个闭环反馈回路在时域上把信号的 RMS 电平钉在参考值附近。它不关心波形内容只关心“这段信号有多响”。做数字接收机的工程师、做雷达回波预处理的学生、以及刚把 Matlab 装好准备跑通信仿真的初学者都会遇到这个需求。本文从 AGC 的数学原理说起给出一份可以直接保存为 .m 文件的基线代码再逐步加上攻击释放、双环结构这类工程改进最后用 Matlab 的仿真工具验证效果。读完这套内容你能自己写出一个能应对衰落和突发的 AGC 模块也明白参数到底该怎么调。2. 用滑动窗口把 AGC 的数学基础立住2.1 AGC 闭环的三个动作估计功率、求增益、回送相乘数字域 AGC 本质上是一个离散时间反馈系统。它每时每刻都在做三件事第一从输入信号里估计当前的功率水平第二拿这个估计值和参考电平做比较算出需要补多少增益第三把增益作用到信号上输出给后面的模块。功率估计最常用的是 RMS均方根方法。对一段长度为 N 的采样序列RMS 定义如下% 对最近 N 个采样求 RMS window signal(idx-N1 : idx); rms_now sqrt(mean(window .* conj(window)));conj是取共轭复数信号用window .* conj(window)得到瞬时功率再取平均。这个做法的好处是它和信号调制方式无关QPSK 也好、OFDM 也好RMS 都能反映“总体响度”。用峰值来做估计则不稳定峰均比PAPR高的信号会让增益永远满足不了要求。求得 RMS 之后增益的计算就是一个除法gain ref_level / rms_now;这里ref_level是目标 RMS 电平。如果输入信号 RMS 是 0.01目标电平是 0.1那增益就是 10。这个增益直接乘到当前采样上输出信号就“听起来”更响了。但工程上不能用这种瞬时方式直接更新增益。原因在于真实信号每一帧的 RMS 都在抖动直接相除会让增益跟着毛刺剧烈跳动输出波形反而被调制出额外包络。所以 AGC 环路里必须加一个低通滤波把增益的变化速度限制住。这个限制环节决定了 AGC 的响应速度也就是后续要说的攻击时间和释放时间。2.2 基线实现把 RMS 估计和增益回送写成 Matlab 循环把上面的数学式子拼起来就是一段能在 Matlab 里直接运行的基线 AGC。这段代码不考虑效率先把环路结构写清楚function [y, gain_log] agc_baseline(x, fs, ref_level, win_len, alpha) % AGC 基线实现滑动窗口 RMS 估计 一阶低通增益更新 % 输入 % x 输入信号列向量 % fs 采样率Hz用于换算时间常数 % ref_level 期望输出的 RMS 电平 % win_len RMS 估计窗口长度采样点 % alpha 增益平滑系数0~1越小变化越慢 % 输出 % y 经过 AGC 后的信号 % gain_log 每一时刻的增益记录用于画图和分析 x x(:); N length(x); y zeros(size(x)); gain_log zeros(size(x)); gain_prev 1.0; % 初始增益设为 1也可以设成上电默认值 for n 1:N % 1. 用滑动窗口估计输入 RMS idx_start max(1, n - win_len 1); window x(idx_start : n); rms_now sqrt(mean(window .* conj(window))); % 2. 防止除零 if rms_now 1e-12 rms_now 1e-12; end % 3. 计算理想增益 gain_target ref_level / rms_now; % 4. 一阶 IIR 平滑攻击和释放用同一个系数 gain_prev (1 - alpha) * gain_prev alpha * gain_target; % 5. 施加增益 y(n) x(n) * gain_prev; gain_log(n) gain_prev; end end逻辑上这个循环做了五步滑窗求 RMS、防止除零、计算目标增益、用一阶低通平滑、把增益乘到信号上。alpha是核心参数它表示“每次采样向目标增益靠近多少”。alpha 1时环路完全无记忆增益变成瞬时值输出波形被严重调制alpha 0.01时增益变化很慢输出比较平滑但对突发信号反应迟钝。参数推荐范围作用过大过小ref_level0.01 ~ 0.3设定输出信号的 RMS 电平输出削顶风险高输出信噪比浪费win_len50 ~ 500功率统计时长响应慢瞬时功率跳动大alpha0.005 ~ 0.1增益平滑速度增益抖动明显跟不上信号起伏以fs 1e6、窗口长度 100 为例功率估计窗口正好是 100 微秒。对大多数窄带通信信号这个窗口能把载波周期都平均掉又不至于太慢。ref_level的取值则要看后面接的是什么模块如果是 QPSK 解调器ref_level通常取 0.1 到 0.2这样在满幅 ±1 的浮点信号下留足峰值余量。2.3 环路稳定性alpha 系数就是 AGC 的时间常数上面代码里最值得展开的是alpha。一阶低通滤波器的传输函数是H(z) alpha / (1 - (1-alpha) * z^-1)它的时间常数 τ 和alpha的关系是tau -1 / (fs * log(1 - alpha)); % 单位秒如果fs 1e6、alpha 0.01τ 约等于 100 微秒。这意味着 AGC 大概用 3 到 5 个 τ0.3~0.5 毫秒才能把增益调整到位。反过来如果想指定时间常数用下面的式子反推alpha 1 - exp(-1 / (fs * tau));这里有个初学者常犯的错直接用1/(fs*tau)当alpha。当fs * tau数值很大时两者差不多但在高速采样率下误差会被放大导致实际收敛速度和预期偏差很大。使用指数形式的换算才是自洽的。稳定性方面alpha在 0 到 2 之间环路都是稳定的超过 2 就振荡。实际应用当然不会取那么大但在做定点化或浮点转固定点时要注意alpha被量化后如果偏大环路会出现极限环。这个细节在纯浮点 Matlab 仿真里看不见等转到 C 或者 FPGA 上才暴露。3. 从基线版到工程级 Matlab AGC三处关键改动3.1 改动一增益限幅和归一化防止无限放大基线代码里有个隐患如果输入信号长时间为零rms_now被钳位到1e-12理想增益就会飙到1e13量级。虽然在下一个非零采样到来时增益会被拉回来但拉回来的过程本身会产生一个巨幅瞬态把后面的滤波器打懵。限制增益范围是最直接的防护手段同时也要考虑实际系统里可变增益放大器VGA的动态范围。模拟前端的增益调节范围有限数字 AGC 计算出的增益如果超出该范围只是画饼。gain_max 10^(60/20); % 60 dB 增益上限 gain_min 10^(-40/20); % 40 dB 衰减下限 gain_target min(max(gain_target, gain_min), gain_max);dB 和线性倍数的换算关系gain_linear 10^(gain_db/20)。写代码时统一用线性倍数计算只在分析时转成 dB 表示。增益限幅放在平滑之前还是之后都行放在之前可以让平滑过程不被超范围的突变拉走收敛更干净。3.2 改动二攻击和释放采用非对称系数真实世界的信号波动不是对称的。信号突然变强是坏事——它会引起削顶必须快压信号突然变弱则没那么紧急——只是信噪比变差可以慢放。这就是 AGC 里经典的攻击/释放Attack/Release非对称处理思想。% 攻击快释放慢 if gain_target gain_prev alpha_used alpha_attack; % 比如 0.3 else alpha_used alpha_release; % 比如 0.005 end gain_prev (1 - alpha_used) * gain_prev alpha_used * gain_target;攻击系数取 0.1~0.3对应微秒级响应释放系数取 0.001~0.01对应毫秒级响应。这样设计后遇到脉冲干扰时 AGC 能快速压住幅度干扰过去后再慢慢恢复增益避免输出信号出现明显的“喘息”效应。用表格对比一下对称与非对称的效果场景对称系数非对称系数突发强干扰增益下压速度慢有削顶增益快速下压保护后级干扰消失增益恢复快包络起伏大增益缓慢恢复输出平稳语音/数据混合信号两段都不合适攻击快、释放慢动态范围控制好攻击释放参数的选择还和调制方式有关。OFDM 信号峰均比高攻击要更慢一些避免 AGC 跟着单个 OFDM 符号的峰值抖动单载波 QAM 则可以适当加快攻击。3.3 改动三双环 AGC慢环路锁电平、快环路抓突发单环 AGC 有个矛盾环路带宽决定了对功率变化的反应速度但带宽过大又会把信号本身的幅度调制AM 分量也压掉这对 AM 解调或者带包络信息的调制方式是致命的。工程上常见的折中方案是用双环结构一个极慢的环路负责把长时间平均功率拉到参考点另一个快环路只在检测到功率突变时介入。function [y, g_slow, g_fast] agc_dual_loop(x, fs) % 双环 AGC 简化实现 % 慢环持续跟踪平均功率时间常数约 200 ms % 快环当信号强度突变超过 10 dB 时介入时间常数约 1 ms N length(x); y zeros(N, 1); g_slow ones(N, 1); g_fast ones(N, 1); tau_slow 0.2; % 慢环时间常数 200 ms tau_fast 0.001; % 快环时间常数 1 ms a_slow 1 - exp(-1/(fs*tau_slow)); a_fast 1 - exp(-1/(fs*tau_fast)); ref 0.1; % 目标 RMS gain_s 1.0; gain_f 1.0; rms_avg 0; threshold_db 10; % 快环介入门限 for n 1:N % 慢环功率估计使用极长窗口 rms_avg (1 - a_slow) * rms_avg a_slow * abs(x(n)); if rms_avg 1e-12, rms_avg 1e-12; end gain_s (1 - a_slow) * gain_s a_slow * (ref / rms_avg); % 快环检测瞬时功率偏离慢环的程度 rms_inst abs(x(n)); if rms_inst 0 dev_db 20 * log10(rms_inst / max(rms_avg, 1e-12)); else dev_db -inf; end if dev_db threshold_db gain_f (1 - a_fast) * gain_f a_fast * (ref / rms_inst); else gain_f 1.0; % 不干预 end g_slow(n) gain_s; g_fast(n) gain_f; y(n) x(n) * gain_s * gain_f; end end参数说明tau_slow 0.2秒意味着慢环在 1 秒内只完成几次有效调整它只响应信道的大尺度衰落threshold_db 10表示瞬时功率高出平均水平 10 倍时才触发快环这能识别出脉冲干扰这样的瞬态事件。快环的增益是独立叠加在慢环之上的事件结束后gain_f会自动回到 1不影响慢环的长期跟踪。双环结构的代价是多了一组参数要调而且快环的触发电平不能设得太低否则正常的信号峰均比波动也会让它频繁介入反而引入谐波失真。一般从 10 dB 起步根据实际信号的峰均比往上加。4. 在 Matlab 里搭建信道场景验证 AGC 性能4.1 先构造一个带衰落和突发的测试信号验证 AGC 不能只用恒定幅度信号那测不出环路的动态行为。常见的做法是生成一个单音或 QPSK 信号叠加瑞利衰落包络再在中间插入一段突发干扰。fs 2e6; % 采样率 2 MHz t (0:fs*0.1-1)./fs; % 100 ms 数据 fc 50e3; % 载波 50 kHz x cos(2*pi*fc*t); % 单音测试信号 % 模拟慢衰落1/Ts 为衰落带宽 fade_freq 20; % 20 Hz 正弦衰落 fade_env 0.3 0.7 * abs(sin(2*pi*fade_freq*t)); % 0.3~1.0 波动 x_fade x .* fade_env; % 在 40~45 ms 处插入 4 倍幅度的突发 burst_region (t 0.04) (t 0.045); x_burst x_fade .* (1 3 * burst_region);fade_env用正弦取绝对值来模拟一个 20 Hz 的时变包络动态范围约 10 dB突发段把信号幅值放大到原来的 4 倍约 12 dB考验 AGC 的攻击速度。4.2 画功率曲线和增益曲线直接观察收敛行为运行 AGC 之后用movmean或者直接画20*log10(abs(y))看输出包络是否平整。下面这段代码把输入输出和增益画在同一张图上[y, gain_log] agc_baseline(x_burst, fs, 0.1, 100, 0.02); figure(Color, w, Position, [100 100 800 600]); subplot(3,1,1); plot(t*1e3, 20*log10(abs(x_burst) 1e-9)); ylabel(输入功率 (dB)); title(输入信号包络衰落 突发); grid on; ylim([-80 0]); subplot(3,1,2); plot(t*1e3, 20*log10(gain_log 1e-9)); ylabel(增益 (dB)); title(AGC 增益变化过程); grid on; subplot(3,1,3); plot(t*1e3, 20*log10(abs(y) 1e-9)); ylabel(输出功率 (dB)); xlabel(时间 (ms)); title(AGC 输出包络); grid on; ylim([-80 0]);20*log10(abs(x) 1e-9)里的1e-9是为了防止log10(0)产生-Inf画图时出现断线。看输出包络的平坦程度能快速判断 AGC 是否工作正常理想情况下输出包络应该是一条平滑的水平线只在突发起始处有小的凹坑——那是攻击过程的痕迹。4.3 用 EVM 和幅度波动评价 AGC 质量对调制的信号功率包络平坦还不够还要看解调星座图的收敛情况。误差矢量幅度EVM是衡量信号质量的通用指标计算公式如下% 假设有理想参考符号 ref_sym 和接收符号 rx_sym err rx_sym - ref_sym; evm_rms sqrt(mean(abs(err).^2) / mean(abs(ref_sym).^2)) * 100; % 单位 %EVM 越低说明 AGC 引入的失真越小。基线 AGC 在alpha 0.02时信号包络波动可能控制在 ±0.5 dB 以内但对于 64-QAM 信号EVM 要求至少低于 8%这时攻击系数就不能太大否则每个符号的能量差异会被瞬时增益调制放进去。指标计算公式好的范围说明输出包络波动max(20log10(abs(y))) - min(...) 1 dB观察平稳段的波动收敛时间从阶跃开始到误差 1 dB攻击 1 ms释放 50 ms用阶跃信号测量EVM见上式QPSK: 10%64QAM: 8%需要星座图对齐测量收敛时间可以用一个幅度阶跃信号前 10 ms 输出幅度 0.1后 10 ms 突跳到 1然后看 AGC 增益曲线从突变点到稳定值的时间间隔。这就是后面章节要讲的“用阶跃响应定标”。5. 参数调优技巧用阶跃响应定标注意三个工程坑5.1 用阶跃信号给 AGC 定标不再瞎试参数AGC 的参数互相牵扯逐个试会浪费一晚上。我一般会先用一个理想阶跃信号把环路的基本响应标定出来再放到真实信道信号里微调。操作方法是生成一段信号前半段幅度 0.1后半段幅度 1.0在交界处加一个陡峭的跳变。运行 AGC 后看增益对数曲线从跳变点到增益第一次进入目标值 ±1 dB 的时间差就是攻击时间反过来从高幅度跳回低幅度测的就是释放时间。用这种方式标定后的参数有一个明确物理含义到了和硬件联调阶段直接把时间常数换算成寄存器值即可不用再猜。5.2 坑一浮点仿真通过定点就不行Matlab 默认跑双精度浮点AGC 的乘法永远不溢出但真实系统早就定点化了。定点化的关键是把增益的更新公式改成定点格式gain用 Q1.15 或 Q1.31alpha用 0~1 的定点小数。最容易出问题的是gain_prev的更新(1-alpha) * gain_prev alpha * gain_target在浮点里没问题定点化后1-alpha的舍入误差会累积长时间运行增益会漂移。解决方法是每 N 个采样做一次溢出检查或者在环路里定期把增益强制拉回 1 附近。验证定点行为时用 MATLAB 的 Fixed-Point Designer 工具箱把同一个信号分别跑浮点和定点版本对比输出之差误差超过 0.1 dB 就要检查位宽。5.3 坑二增益跳变带来频谱泄漏即使 AGC 的输出幅度平稳增益突变本身也会在频谱上产生毛刺。当一个突发信号到来攻击环路迅速把增益从 20 dB 压到 0 dB增益不是一个直流电平的衰减而是一个快速变化的调制函数它会和信号相乘产生新的频谱分量。这是很多接收机 AGC 后出现带外杂散的根源。要抑制它除了用更平滑的增益变化曲线还可以把增益变化率做一个斜率限制slew rate limit。在 Matlab 里实现就是在每步更新后检查增益变化量是否超过某个阈值超过就截断。max_step_db 0.5; % 每次采样最多变化 0.5 dB gain_step_db (20*log10(gain_new) - 20*log10(gain_old)); if abs(gain_step_db) max_step_db gain_new gain_old * 10^(sign(gain_step_db)*max_step_db/20); end这个限制会稍稍延长攻击时间换来的是干净的频谱。采样率越高每步允许的 dB 数可以越大最终的时间分辨率还是由环路的时间常数决定。5.4 坑三AGC 状态复位和初始化顺序最后一个坑藏在系统集成时AGC 不是独立模块它和 A/D 前端的直流偏置校正、NCO 混频、降采样滤波器一起工作。在很多软件无线电设备里AGC 的状态gain_prev、rms_avg需要在接收机启动时清零否则旧参数会直接作用在新信号上造成起始端的剧烈瞬态。另外建议在 AGC 模块里加一个reset输入端口当信道切换、频率跳变时由上位机触发复位比让 AGC 自己慢慢收敛要省时间。尤其是 TDMA 这类突发通信体制每次突发到来都重新初始化 AGC 状态往往比连续跟踪得到更好的误码率——突发很短环路根本来不及收敛倒不如用一个开环增益表直接查表这才是突发通信场景里真正高效的做法。本文还有配套的精品资源点击获取