资讯动态

ADC动态指标计算:基于MATLAB的SNR/SNDR/THD/SFDR分析程序

发布时间:2026/10/16 3:48:17 来源:尧图企业网站定制
简介用于计算信噪比SNR、信号噪声加失真比SNDR、总谐波失真THD、有效位数ENOB和无杂散动态范围SFDR的MATLAB脚本面向信号质量评估、ADC性能测试及相关课程设计场景适合需要量化分析频域参数的工程师和初学者。压缩包共2个文件包含1个M代码脚本与1个TXT数据文件整体仅4KB脚本从TXT读取原始序列完成滤波与加窗预处理再借助FFT频谱分析分离谐波与杂散分量最终输出各项指标并给出可复现的分析流程。已有4556人浏览学习用户可对照代码深入理解各指标的物理意义与算法差异也可将脚本迁移至自己的采集数据中进行快速验证。压缩包体量虽小却覆盖了信号链性能评估的核心计算方法对硬件调试、算法优化与理论学习均有直接参考价值。1. SNR的MATLAB计算程序先把采样条件理顺指标才有意义说到SNR的MATLAB计算程序做ADC测试或信号链调试的工程师都会先想到那几个公式但实际跑过一遍就知道同样的采集数据换一个FFT点数、换一种窗函数、甚至噪声bin的选取范围不同算出来的SNR、SNDR、THD和ENOB能差出好几个dB。这套程序的价值不在于公式本身而在于把相干采样、频谱预处理、谐波识别这些前置条件全部封装好输入采样序列后直接得到五个动态指标以及定位好的频谱信息。适合需要评估ADC、DAC、Sigma-Delta调制器输出数据或是在数字信号链里想快速确认性能边界的从业者。2. 相干采样与FFT预处理在算SNR之前先把频谱“摆正”2.1 相干采样的数学条件与三个数值约束动态参数计算的第一步不是打开MATLAB敲FFT而是先确认采样数据是否满足相干采样条件。相干采样的定义式很简洁Fin / Fs M / N其中Fin为输入信号频率Fs为采样率N为FFT点数M为采样窗口内的整数周期数。工程上更看重三个约束M和N必须是正整数M与N互质且M * Fin落在第M个FFT bin上bin索引M1因为第1个bin是直流。互质的目的是让量化噪声、时钟抖动等非理想因素尽可能均匀分布到各个频点避免周期性噪声堆叠在部分bin上造成假杂散。实际选参数时我一般先定N比如4096或8192再根据Fin/Fs的比例找一个接近的M。不要随手把Fin设成1MHz、Fs设成10MHz就开跑那样得到的频谱是不对齐的后面所有指标都会带上频谱泄漏误差。2.2 窗函数的选择相干采样下也未必能省很多教程说“相干采样不需要加窗”这句话只对理想正弦源成立。ADC前端的时钟抖动、PLL杂散、电源耦合噪声会让实际采样频率和理想Fs有微小偏差这种情况下不加窗的频谱旁瓣会掩盖低幅度杂散导致SFDR和THD被严重低估。常见做法是如果数据来源是理想的仿真输出确定已经严格满足相干采样条件用矩形窗即不加窗保留最高频率分辨率如果数据来自真实采集卡或带有同步误差优先选Hann窗或Blackman-Harris窗。Hann窗主瓣宽度约4个bin对谐波幅度影响在0.1dB以内适合SNR估算。Blackman-Harris窗旁瓣抑制更好但主瓣更宽对间隔很近的杂散分辨力下降。加窗会在幅度谱上引入相干增益损失计算时必须做幅度修正。代码里我通常把Hann窗的修正系数设为2即幅值乘以2再参与功率求和这样信号幅度的读取与不加窗时一致。2.3 直流分量与频谱排序功率计算前的小事FFT结果里第一个bin是直流分量。如果ADC输入端存在直流失调这个bin的功率会非常大不剔除的话噪声功率虚高、SNR被拉低。计算动态指标时直流bin必须从所有功率求和中排除并且不能计入杂散。另外要处理的是单边谱与双边谱的换算。MATLAB的fft输出是双边谱取前N/21个点后除了直流和奈奎斯特点之外的所有bin幅度都要乘以2才是单边谱线性幅度。这个步骤很多人漏掉漏掉后信号幅度显示只有实际值的一半THD和SFDR的分贝值反而不受太大影响但在同时打印信号幅度时会对不上数据手册。% 单边谱幅度修正示例 X fft(x); N length(x); mag abs(X(1:N/21)) / N; mag(2:end-1) 2 * mag(2:end-1);这段代码做了幅度归一化与单边谱换算。除以N是为了让FFT结果对应正弦波的真实幅度对中间所有bin乘2是因为双边谱能量分散在正负频率两侧取单边后要合并。直流bin和奈奎斯特bin末端最后一个点不乘2。3. SNR、SNDR、THD、SFDR和ENOB的计算流程从幅度谱到五个指标3.1 FFT幅度谱的规范化与bin定位拿到频谱后第一步是定位信号所在bin。在相干采样条件下信号bin索引直接等于M1但为了兼顾非相干数据我一般写成峰值搜索在预期信号bin附近搜索幅度最大值并返回精确索引便于后续谐波定位。实际使用中直接取整数值很快。如果用了非整数倍周期采样就会出现栅栏效应信号峰值落在两个bin之间此时应当在信号bin和紧邻的两个bin之间做抛物线插值估计真实峰值幅度。插入代码示例function result calc_adc_dynamic(x, Fs, M, numH) N length(x); X fft(x); mag abs(X(1:N/21)) / N; mag(2:end-1) 2 * mag(2:end-1); if M 0 sig_bin M 1; % 相干采样直接定位 else [~, sig_bin] max(mag(2:end-1)); % 搜索峰值去掉DC sig_bin sig_bin 1; end sig_amp mag(sig_bin); sig_power sig_amp^2;参数说明x为输入采样序列Fs为采样率M为采样窗口内信号周期数numH为要计入THD的谐波次数。M传0时启用峰值搜索模式。sig_power这里直接用幅度平方表示功率没有除以2原因是后续噪声和谐波也用同样的幅度平方累加比值与分贝差不受影响代码更简洁。3.2 谐波分量识别与THD计算谐波位置理论上在k*M1k2,3,...但实际硬件数据可能因非线性产生轻微偏移。绝大多数ADC测试场景谐波精确落在整数bin上直接按倍数找bin即可。需要确认的是谐波是否超过奈奎斯特频率一旦超过会混叠回基带位置不再是线性倍数关系。这时要先将谐波频率对Fs取模再映射到bin索引。下面是完整谐波聚合流程harmonic_bins zeros(1, numH - 1); harmonic_power 0; for k 2:numH f_harm k * M * Fs / N; % 第k次谐波频率 f_alias mod(f_harm, Fs); if f_alias Fs / 2 f_alias Fs - f_alias; % 折叠回奈奎斯特区间 end bin_h round(f_alias / Fs * N) 1; if bin_h 2 bin_h N/2 harmonic_bins(k-1) bin_h; harmonic_power harmonic_power mag(bin_h)^2; end end说明谐波的频率搜索范围在kM±2个bin内但工程上相干采样的理想数据直接锁定kM1即可。谐波功率累加时用幅度平方并且不对超出奈奎斯特范围的谐波做二次混叠校正以外的事。需要留意某些谐波可能与信号bin或其它杂散重合那会导致THD偏大后面避坑部分再单独说。3.3 SNR与SNDR的区别及噪声功率计算这是整个程序中最容易理解错的地方。SNDR计算的是信号功率与噪声加失真总功率之比分母包含所有非信号、非直流的bin功率谐波也在里面而SNR的分母是噪声功率不包括谐波但通常包括随机噪声和除了规定谐波以外的有界杂散具体取决于测试标准。我的程序实现如下total_power sum(mag(2:N/2).^2); % 排除直流与奈奎斯特 noise_power total_power - sig_power - harmonic_power; SNR 10 * log10(sig_power / noise_power); SNDR 10 * log10(sig_power / (noise_power harmonic_power));注意如果同时存在多个非谐波杂散按IEEE标准它们应纳入SNR的噪声项。有些规范要求SNR分母只含噪声不含杂散但工程上分不清杂散来源时通常把非谐波杂散算入SNR分母并将最大杂散单独报告为SFDR。3.4 SFDR与ENOB的换算SFDR定义是信号功率与最大杂散功率之比。这里的最大杂散指除信号和谐波以外的最大频谱分量但如果杂散恰好落在谐波位置算不算谐波就有讲究。我的原则是只要不是信号本身的整数倍频率一律当杂散看最大杂散搜索范围覆盖非信号、非指定谐波的全部频点。spur_mask true(size(mag)); spur_mask(sig_bin) false; spur_mask(harmonic_bins(harmonic_bins 0)) false; spur_power max(mag(spur_mask).^2); SFDR 10 * log10(sig_power / spur_power); ENOB (SNDR - 1.76) / 6.02;ENOB是从SNDR换算来的有效位数公式的1.76dB对应理想量化器的信噪比下限6.02dB每bit的斜率只适用于满幅正弦输入。换算时如果SNDR单位是dBc直接用如果是dBFS则要先加满幅回退值修正否则ENOB会偏低。大多数数据手册的SNDR是dBc直接算即可。4. 避坑相干采样、量化器与幅度的五个常见问题4.1 FFT点数N与信号周期数M不互质SNR起伏不定现象同样一组原始数据将N从4096改成8192SNR变化超过3dB。原因如果M和N不互质量化噪声会周期性地聚集在某些bin上等效于叠加了一个人为杂散噪声功率被高估。这也是为什么手册上给出的性能参数总是在“互质相干”条件下测量的。解决选参数时先计算最大公约数保证gcd(M, N) 1。MATLAB中直接检查gcd(M, N)不为1时调整M或N。程序内可以加一条警告语句检测到不互质就提示“条件不满足相干采样结果仅供参考”。4.2 信号幅度过大导致ADC削波THD虚高现象输入正弦波幅度接近满幅计算出的THD突然从-80dBc恶化到-50dBc。原因ADC输入超出线性范围输出信号出现限幅削波。削波产生大量高次谐波并且能量分布与输入幅度不呈线性关系。解决回退输入幅度至少1dBFS多数ADC数据手册的测试条件是在-1dBFS到-6dBFS之间。验证方法将输入幅度降低6dB后重新计算如果THD基本不变或改善超过6dB则存在削波问题。程序可以将输入序列的最大值与量化满幅比较自动提示是否接近削波边界。4.3 量化器位数设置不当算法结果与理想值对不上现象用理想16bit量化器生成数据SNR算出来只有50dB与理论值98dB差太多。原因数据可能被当作浮点连续信号处理了并没有量化过程或者量化范围设置不对比如16bit ADC采样满幅为±1但信号幅度只有0.01实际有效量化位数远低于16bit。解决确认仿真的量化器输出是否真正经过舍入。范围设置上信号幅度至少要覆盖量化器满幅的50%以上理想情况下满幅正弦输入时理论SNR约(6.02 * N 1.76)dB。程序在导入数据时增加一个建议值显示提醒使用者确认量化步长与信号幅度的匹配关系。4.4 谐波范围选取不当THD与SFDR互相污染现象固定取前10次谐波但ADC输出存在明显的21次谐波THD结果反而比只取前5次时更好。原因超过奈奎斯特频率的谐波发生混叠混叠分量恰好落在信号附近或与大杂散重叠。THD分母中混叠谐波功率被重复计算或某个强混叠谐波被排除在谐波集合之外进入SFDR搜索范围导致SFDR误报。解决先绘制频谱图观察谐波分布确定主要谐波次数范围再设置numH参数。常规ADC测试取2到9次谐波但宽带Sigma-Delta调制器可能需要取到20次以上。程序支持手动输入谐波次数同时会输出各次谐波功率表方便核对。4.5 直流分量与奈奎斯特点处理错误现象SNR在不同采样率下结果不一致但信号频率和幅度都没变。原因FFT的直流bin直接计入噪声功率或者奈奎斯特频率bin未乘2导致幅度被低估最直接的影响是ENOB计算结果偏移0.1到0.3bit。解决统一在功率求和时排除直流bin和奈奎斯特点。该处理在程序内部已完成但如果使用者拿这段代码去改其它脚本很容易在循环边界少写一个点。建议所有频谱处理脚本都写成mag(2:end-1)从索引上杜绝边界问题。5. 从脚本到函数输入输出结构设计与批量处理5.1 输入参数的定义与默认值上面已经给出了核心计算逻辑实际工程使用时要封装成带默认参数的函数避免每次测试都重新设置一堆变量。推荐输入参数分组为信号参数、FFT参数、算法参数、数据格式参数。function result analyze_adc_dynamic(x, params) arguments x double params.Fs double 10e6 params.M double 0 params.N double length(x) params.numH double 10 params.window char rect params.inputRange double 1.5 end % 默认M0表示自动峰值搜索 % window支持rect/hann/bharguments块是MATLAB R2019b之后的语法用于声明默认值并校验输入类型。使用params.Fs10e6的好处是所有可调参数都集中在一个结构体里调用时按需修改不会出现脚本里到处都是全局变量。5.2 输出结构体的组织输出结果建议统一使用结构体字段名与数据手册对应方便批量后处理和生成报告。字段设计如下result.SNR: SNR值单位dBc result.SNDR: SNDR值单位dBc result.THD: THD值单位dBc谐波功率与信号功率之比 result.SFDR: SFDR值单位dBc result.ENOB: ENOB值单位bit result.signalBin: 信号所在bin索引 result.signalAmp: 信号线性幅度 result.harmonicBins: 谐波bin索引数组 result.harmonicPower: 各次谐波功率 result.spurBin: 最大杂散bin索引 result.spurAmp: 最大杂散线性幅度 result.spectrumMag: 修整后的单边幅度谱保留spectrumMag是为了让调用方直接绘图不用重算FFT省掉每次调试都要复制频谱修正代码的重复操作。harmonics数组单独列出也便于与频谱图核对。5.3 批量导入数据CSV、MAT、WAV三种来源实际测试最常见的数据来源是逻辑分析仪导出的CSV、MATLAB仿真保存的MAT文件、或者音频采集卡的WAV文件。批量处理时先做一个统一的载入层自动识别文件类型并返回标准化数据function x load_signal_from_file(filename) [~, ~, ext] fileparts(filename); switch lower(ext) case .csv raw readmatrix(filename); x raw(:, 2); % 第二列为采样数据 case .mat S load(filename, x); x S.x; case .wav [x, Fs] audioread(filename); otherwise error(不支持的文件格式); end end这里需要注意CSV文件常常包含时间戳或电压单位等额外信息直接readmatrix会把第一列时间当数据读进来所以强制取第二列。MAT文件必须检查变量名习惯上将变量命名为x但不同工具导出的变量名可能不同可以先用who查看再动态获取。批量测试时我一般建一个文件名清单循环调用主函数并将所有指标汇总为一个表格fileList dir(data/*.csv); results struct(); for i 1:length(fileList) x load_signal_from_file(fullfile(fileList(i).folder, fileList(i).name)); r analyze_adc_dynamic(x, struct(Fs, 10e6, M, 421, numH, 10)); results(i).name fileList(i).name; results(i).SNR r.SNR; results(i).SNDR r.SNDR; % 按需保留更多字段 end T struct2table(results); writetable(T, dynamic_metrics_summary.csv);批量处理时最容易出错的是每份数据的采样率或输入频率与参数不匹配。建议在载入函数返回值中附带原始采样率信息并在主函数里加入断言校验如果数据的长度与声明N不一致自动截断或报警而不是静默计算出一个错误结果。6. 用理想量化器验证算法正确性再延伸报告与出图6.1 理想ADC数据生成与参数自检验证算法最快的方式是构造一个理想16bit ADC模型用相干采样生成无噪声正弦数据程序计算出的SNR应当逼近理论值98.08dB。若偏差超过0.5dB说明频谱修正或噪声bin范围有问题。N 4096; M 421; Fs 10e6; Fin M / N * Fs; t (0:N-1)./Fs; x sin(2*pi*Fin*t); Q 2^15; % 16bit有符号量化半码 xq round(x * Q) / Q; r analyze_adc_dynamic(xq, struct(Fs, Fs, M, M)); fprintf(SNR%.2fdB, SNDR%.2fdB, ENOB%.2fbit\n, r.SNR, r.SNDR, r.ENOB);这段验证代码里用的量化方式是最小值加均匀量化。生成的数据没有加噪声因此SNDR与SNR差异极小THD应低于-100dBc。看到ENOB在15.9bit以上就说明链路通了。6.2 进一步自查加入削波与谐波观察指标联动验证算法正确解析数据后再做两个破坏性测试将信号幅度提高6dB迫使削波发生观察THD明显恶化而SFDR可能变化不大在信号中注入一个单频杂散确认SFDR准确报告该杂散幅度而SNR不受影响或按噪声谱密度计入。这一步能直观理解各指标如何对同一条频谱做不同切片。6.3 从指标到报告一次绘制频谱图加关键标注最后建议把绘制频谱和标注谐波、信号、杂散的逻辑写进同一函数每次跑完数据直接输出一张带参数标注的频谱图方便在测试记录里贴图。从那以后我每次算SNR之前都会强制走一遍验证流程先生成理想量化数据确认基线再叠加一个已知杂散确认SFDR识别没有问题最后才把跑测试数据的结果当作可信值写入报告。这个习惯帮我挡掉了至少三次因为参数设置错误导致的数据误判。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑