摘要:ADC作为连接模拟世界与数字世界的桥梁,是现代雷达系统中必不可少的一环,其性能的好坏直接决定雷达的好坏。激光测风雷达作为现代雷达系统大家庭中的一员,必然也会使用到ADC。为了保证其性能,除了良好的设计外,设计完成之后充分的测试也是必不可少的。

在测试之前,首先简单介绍下ADC的种类以及其主要指标。

ADC种类

 

类型

优点

缺点

逐次逼近型

速度高,功耗低,低分辨率价格低

分辨率提升困难

Σ-Δ调制型

采样精度高,无需抗混叠滤波器

应用带宽低

并行比较型/串并行型

转换速率快

分辨率低,规格大,价格高

压频变换型

分辩率高、功耗低、价格低

需要外部计数电路共同完成AD转换

不同类型的ADC由于其原理不同,各有优缺点,实际使用过程中可以根据具体使用场景选择最合适的类型。下图列举了一些典型应用及其对采样率和分辨率的要求。

python 高速ADC测试_采样频率

ADC指标

 

不管何种原理的ADC,其评价指标是一样的,主要分为静态指标和动态指标,其定义及意义如下表所示

指标名称

定义及意义

静态指标

偏移误差(Offset Error)

输入信号为零时输出信号不为零的值

增益误差

(Gain Error)

增益误差是预估传递函数和实际斜率的差别,增益误差通常在模数转换器最末或最后一个传输代码转换点计算

积分非线性(INL)

INL表示了ADC器件在所有的数值点上对应的模拟值,和真实值之间误差最大的那一点的误差值

差分非线性(DNL)

相邻两刻度之间最大的差异

动态指标

分辨率

分辩率又称精度,通常以数字信号的位数来表示

有效位数(ENOB)

实际处理中,ADC能够达到的处理精度。根据信纳比算得。

信噪比(SNR)

输入信号和噪声(不包括任何谐波以及直流) 的有效值之比,衡量器件内部噪声大小

总谐波失真(THD)

输入信号与系统所有谐波(HD)的总功率(P)之比,提供系统对称和非对称非线性产生的总失真大小

无杂散动态范围(SFDR)

基本频率与杂波信号最大值的差值,能够对系统失真进行量化

信纳比(SINAD)

输入信号和所有输出信号失真(包括谐波成分,不包括直流)的功率之比,评估输出信号所有传递函数非线性加上系统所有噪声(量化、抖动和假频)的累积效果

一般来讲静态参数都比较好理解,而下面这张图及其中公式可以让你对各个动态指标有一个更直观的认识。

python 高速ADC测试_高速ADC_02

高速ADC测试

在测试之前,要做好充足的准备工作,本文档只对ADC单频测试进行讨论,一个比较好的测试系统框图如下图所示,测试过程中需要的仪器有信号发生器、带通滤波器、电源、电脑等。

python 高速ADC测试_数据_03

ADC的偏移误差与增益误差可以很方便通过对采集结果进行计算得到,而积分非线性与差分非线性通常是通过对正弦波的采样数据进行幅度分布的直方图间接统计计算得到,如下图所示,理想正弦波的幅度分布应该是左面的形状,由于非线性等的影响,分布会变成右边的形状,通过对直方图分析可以得出静态参数的指标。

ADC的动态参数是对正弦波的采样数据进行FFT频谱分析间接计算得到,为了在FFT处理时不发生频谱泄露,从而得到更准确的结果,一般采用相干采样的方式,即需选择特定频率的输入信号与采样频率,从而使得采集到的采样数据能在记录长度内转换的代码尽可能多。这是通过输入信号频率与采样频率之间的一种基本关系实现的,其数学关系如下所示:

finfs=NcyclesNsamples

python 高速ADC测试_python 高速ADC测试_04

其中,fin为输入信号频率,fs为采样频率,Ncycles为完整采样的周期数,Nsamples为采样总点数,一般,为方便FFT处理,Nsamples取2的整数次幂,Ncycles取与Nsamples互质的整数。

例如,当时用相干采样时,若设定输入信号频率为85MHz,采样率为400MHz,采样总点数为16384,通过计算,此时,Ncycles为3481.6,取Ncycles为3483,重新计算输入信号频率应为85.03417098MHz。

下图显示了相干采样与非相干采样时FFT的结果,其中红色曲线为非相干采样结果,发生了明显的频谱泄露,从而导致不能得到准确的动态性能计算结果。

python 高速ADC测试_python 高速ADC测试_05

 

ADC的动态参数是对正弦波的采样数据进行FFT频谱分析间接计算得到,为了在FFT处理时不发生频谱泄露,从而得到更准确的结果,一般采用相干采样的方式,即需选择特定频率的输入信号与采样频率,从而使得采集到的采样数据能在记录长度内转换的代码尽可能多。这是通过输入信号频率与采样频率之间的一种基本关系实现的,其数学关系如下所示:

python 高速ADC测试_高速ADC_06

其中,fin为输入信号频率,fs为采样频率,Ncycles为完整采样的周期数,Nsamples为采样总点数,一般,为方便FFT处理,Nsamples取2的整数次幂,Ncycles取与Nsamples互质的整数。

例如,当时用相干采样时,若设定输入信号频率为85MHz,采样率为400MHz,采样总点数为16384,通过计算,此时,Ncycles为3481.6,取Ncycles为3483,重新计算输入信号频率应为85.03417098MHz。

下图显示了相干采样与非相干采样时FFT的结果,其中红色曲线为非相干采样结果,发生了明显的频谱泄露,从而导致不能得到准确的动态性能计算结果。

python 高速ADC测试_python 高速ADC测试_07

当输入信号确实不能完全满足相干采样的要求时,还可以通过加窗的方式减小频谱泄露的影响,不同的窗函数有不同的特性,选择合适的窗函数可以辅助得到理想的结果,比较常用的窗函数有Blankman、Hamming等,可以在MATLAB中使用help window命令查看不同窗的特性与使用方式。 

在测试过程中,输入信号的“纯度”会影响数字输出的性能。输入信号中的耦合噪声将转换为输出信号数字噪声,如果输入信号中有太多噪声和失真,ADC性能实际上会被测试条件所掩盖。输入信号的精度和纯度最终取决于器件的转换分辨率,一般来说测试设备的精度要比被测器件高10倍以上。另外在输入端使用滤波器,可以除去部分输入信号之外的噪声和失真。

通常受测试条件的限制,我们往往没有完美的信号源,信号源自身的谐波与噪声会超过被测ADC的水平,从而影响测试结果。在测试之前,我们可以先使用频谱仪及输入短接的方式对信号源与ADC底噪做一个简单评估。 

python 高速ADC测试_测试_08

在上图中,左侧图像是对采集数据进行FFT的结果,其中红色曲线对应ADC输入短接,该曲线能够能够直接反应ADC自身量化噪声水平;蓝色曲线对应ADC输入信号频率为85.03417098MHz,幅度为8dBm,从结果中可以看到,其底噪水平提高,说明信号源自身噪声水平大于ADC量化噪声,除此之外还存在较明显谐波,对比右侧直接使用频谱仪对信号源测试的结果看,说明二次谐波主要是由信号源自身产生的。

 

 

以上是个人在ADC测试过程中的一些心得,如有错误,欢迎指正, 

下面附测试过程中使用的MATLAB代码

% Test Script for ADC     Dynamatic Parameters
% ZJ 20181220
clear all;
%% SET OPTIONS (These need to be set)
% ********************************************
DNLgo=1;                      % Execute INL/DNL? [yes=1/no=0]
fin = 85.034e6;%17098e6;


%% SET VALUES FOR FFT (These need to be set)
% *********************************************
%co
N=14;                      % Resolution of converter
N_sample = 16384;          % Sampling points ,it's better being power of 2 
fs=399.99995904e6;                  % Sampling frequency
ts= 1/fs;

t = ts*linspace(1,N_sample,N_sample);
w = 2*pi*fin;
%% DATA Generate (These need to be set)
% ******************************
A1 = 2^(N-1);
A2 = 0.0006*A1;
A3 = 0.00005*A1;
V1 = A1*sin(w*t);
V2 = A2*sin(2*w*t);
V3 = A3*sin(3*w*t);

data = V1+V2+V3 + 0.5*randn(1,N_sample);

%data = data - mean(d50.82ata);
datac=data+2^(N-1);
%datac = datac';

%% WINDOW DATA
% ***********************************
% data = data';
% data=data.*window(@blackman,N_sample);   %加窗截断
NFFT = N_sample;

%% Prepare for FFT
frescalc = fs/NFFT;
Ts=1/fs;                   % Calculate period
Ndelay=0;                  % Number of samples to dump at beginning of transient
Vref=2^(N-1);              % Positive max voltage swing for voltage output (Simulink Model)
                           %     or 2^(N-1) for digital output

num_harm=5;                % # of harmonics to find and use in THD/SNR calculation


%% CALCULATE VALUES FOR FFT
% ***********************************

fft_o = fft(data,NFFT)/NFFT;
fft_amp= 2*abs(fft_o(1:NFFT/2));  % multiply x2 to normalize because fft of 1*sin(x) = 0.5[d(w-wo)+d(w+wo)]
fft_amp_nor = fft_amp/Vref;       % normalize to 1
% fft_amp_nor(6967) = 1e-20;
fft_amp_nor_db = 20*log10(fft_amp_nor+1e-20);% change to [dB], add 1e-20 to avoid log 0
f = fs/2*linspace(0,1,NFFT/2);

% CALCULATE MEAN AND STDDEV OF DATA
% *************************************
ave_data=mean(data);
stddev_data=std(data);

% DETERMINE HARMONIC FREQUENCIES
% ***********************************
% Finds the harmoic frequencies as they fold into the baseband
%   as well as the vector index of those harmonics
for i=1:1:(num_harm+1)   % harmonic number
    done=0;
    k=1;     % frequency band number, 1 = 0 to Fs Hz
    while done==0
        if i*fin < (k-1/2)*fs
            harm(i)=i*fin-(k-1)*fs;
            done=1;    
        elseif i*fin < k*fs
            harm(i)=k*fs-i*fin;
            done=1;
        else
            k=k+1;
        end
    end
end
harm_ind=round(harm/frescalc)+ones(1,length(harm)); % determine vector index of harmonics
                                         %   add 1 because DC = index 1 将谐波对应到采样点上
                                         
%% CALCULATE SNDR, SNR, SFDR, THD
% *************************************

    % Calculate SNR/SNDR/SFDR/THD normally
    
    fundpnts=10;                % # of FFT points on either side of fundamental to include
                           %   in fundamental power due to non-coherence(非相干性)
    
    fftout_n=fft_amp_nor;                                %   Save FFT data, use copy for manipulations
    fund_ind=(harm_ind(1)-fundpnts):1:(harm_ind(1)+fundpnts); %第一个谐波(即harm_ind(1)就是信号频)
                                                    %   Points to include in fundemental
    P_S=sum(fftout_n(fund_ind).^2);                 % Power of fundemental
    P_ND=sum(fftout_n(2:N_sample/2).^2)-P_S;        % Power of Noise and Distortion (exclude DC)不包括直流,所以从2开始
    P_D=sum(fftout_n(harm_ind(2:(num_harm+1))).^2); % Power of harmonics 因为第一个谐波(即harm_ind(1)是主频,不包括在内)
    for i=[1 fund_ind]
        fftout_n(i)=1e-20;                          % Remove DC and fundamental from spectrum for SFDR,画出图像可以决定fundpnts的值
    end
    
    
    [M_H,H_ind]=max(fftout_n(1:N_sample/2));        % Magnitude and index of dominant harmonic
    P_H=M_H^2;                                      % Power of dominant harmonic
    H_num=-1;
    for i=1:1:length(harm_ind)                      % Test to deterimine number of 
        if H_ind==harm_ind(i)                       %   dominant harmonic
            H_num=i;                                %   -1 means not one of primary harmonics
        end
    end

    SNDRo  = 10*log10(P_S/P_ND);                       % SNDR [dB]
    THDo   = 10*log10(P_S/P_D);                        % THD [dB]
    SNRo   = 10*log10(P_S/(P_ND-P_D));                 % SNR [dB] 不能包括谐波
    SFDRo  = 10*log10(P_S/P_H);                        % SFDR [dB]
    ENOBo  = (SNDRo-1.72)/6.02;                        % ENOB [Bit]
                                        
%% plot FFT
% *********************************************
%figure
hold on
plot(f(1:NFFT/2)/1e6,fft_amp_nor_db(1:NFFT/2));  % Choose FFT with frequency or index
%plot(frescalc*N_cycle/1e6,fft_amp_nor_db(N_cycle+1),'rs')   
% for i=1:1:length(harm)     % Mark all the harmonics
%     plot(harm(i)/1e6,fft_amp_nor_db(harm_ind(i)),'rs')
% end
ylabel('Full-Scale Normalized Magnitude[dB]')
xlabel('Frequency [MHz]')
title(sprintf('FFT (%g points)\nFs = %g MSps, Fin = %g MHz (%1.2g dBfs)', ...
      NFFT,fs/1e6,fin/1e6,fft_amp_nor_db(harm_ind(1))));
grid;
box on;
ylim([-140 10]);
set(gca,'xgrid', 'off');
set(gca, 'GridLineStyle' ,'-');
set(gca,'yTick',[-140:10:10]);

s1=sprintf('SFDR = %4.2fdB\n',SFDRo);
s2=sprintf('THD   = %4.2fdB\n',THDo);
s3=sprintf('SNR   = %4.2fdB\n',SNRo);
s4=sprintf('SNDR = %4.2fdB\n',SNDRo);
s5=sprintf('ENOB = %4.2fbit\n',ENOBo);
text(0,-10,s1);
text(0,-20,s2);
text(0,-30,s3);
text(0,-40,s4);
text(0,-50,s5);
hold off;  


% % CALCULATE DNL/INL and PLOT
% % *********************************************
% dnl = 0;
% inl = 0;
% if DNLgo==1
%     % Calculate NNL/INL
%     min_bin=min(datac);
%     max_bin=max(datac);
%     h = hist(datac, min_bin:max_bin);          % Histogram各个数据出现的频数统计
%     ch = cumsum(h);                           % Cumulative histogram累积和
%     Tlevels = -cos(pi*ch/sum(h));             % Raised cosine fit 上升的余弦拟合
%     hlin = Tlevels(2:end) - Tlevels(1:end-1); % Difference between adjacent bins相邻项做差
%     hlin = hlin(3:end-2);                     % Dump outside bins
%     lsb = sum(hlin) / (length(hlin));         % Find average difference between bins
%                                               %   to remove gain error
%     dnl = [0 hlin/lsb-1];                     % Remove gain error, center DNL on 0
%     inl= cumsum(dnl);                         % INL is integral of DNL
% 
%     % PLOT DNL/INL
%     figure;
%     title(sprintf('DNL/INL (%g pnt)',length(datac)));
%     subplot(2,1,1)
%     plot(linspace(min_bin+2, max_bin-2, length(dnl)), dnl);
%     title(sprintf('DNL/INL (%g pnt)',length(datac)));
% %    xlabel('Digital Code [LSB]');
%     ylabel('DNL [LSB]');
%     xlim([0 2^N]);
%     ylim([-2 ceil(max(dnl))]);
%     subplot(2,1,2)
%     plot(linspace(min_bin+2, max_bin-2, length(dnl)), inl);
% %    title(sprintf('DNL/INL (%g points)',length(datac)));
%     xlabel('Digital Code [LSB]');
%     ylabel('INL [LSB]');
%     xlim([0 2^N]);
%     ylim([floor(min(inl)) ceil(max(inl))]);
% end

% % DISPLAY DATA
% % ***********************************************
% 
% disp(sprintf('%s%s%s%s%s%s%s%s', ...
%      sprintf('\n\n**********************\n'), ...
%      sprintf(' Fs   = %g MSps\n', fs/1e6), ...
%      sprintf(' Fin  = %g MHz (%1.2g dBfs)\n', fin/1e6, fft_amp_nor_db(harm_ind(1))), ...
%      sprintf(' MEAN = %g LSB\n',ave_data), ...
%      sprintf(' STD  =  %g LSB\n',stddev_data), ...
%      sprintf(' DNL  = -%2.2g/+%2.2g LSB\n', abs(min(dnl)), max(dnl)), ...
%      sprintf(' INL  = -%2.2g/+%2.2g LSB\n', abs(min(inl)), max(inl)), ...
%      sprintf(' Psignal = %g dB\n',10*log10(P_S)), ...
%      sprintf(' SFDR = %g dB (%g, %gMHz)\n',SFDRo,H_num,H_ind*fres/1e6), ...
%      sprintf(' THD  =  %g dB (%g harms)\n',THDo,num_harm), ...
%      sprintf(' SNR  =  %g dB\n',SNRo), ...
%      sprintf(' SNDR = %g dB\n',SNDRo), ...
%      sprintf(' ENOB = %g Bit\n',ENOBo), ...
%      sprintf(' MAX = %g  min = %g \n',max(data),min(data)), ...
%      sprintf('**********************')));
% %************************************************