Analysis of samples captured in ADC1/ADC2 interleaved mode

Dual fast interleaved mode: ADC1 master, ADC2 slave.

$f_{sADC1} = f_{sADC2} = 1 \frac{Msample}{s}$, $t_s = 1.5 \mathrm{cycles}$

Signal generator connected to PA0.

$U_{bias} = \frac{V_{cc}}{2}$

In [1]:
fs = 1e6
nbias = (2**12)/2

20 kHz test

$f_g = 20 \mathrm{kHz}$, $P_g = 0 \mathrm{dBm}$

In [2]:
fg = 20e3
In [25]:
def load_samples(path):
    for line in open(path):
        f = line.split()
    
        if f[0] != 'DS':
            continue
        if f[-1] != 'DE':
            continue
    
        x = array(map(float, f[1:-1]))

        x_adc1 = x[::2]        
        x_adc2 = x[1::2]
        
        return x_adc1, x_adc2
    
x_adc1, x_adc2 = load_samples("test_20khz.log")

$N$ - number of samples taken by each ADC.

In [26]:
N = len(x_adc1)
assert len(x_adc1) == len(x_adc2)

print N
1024

Plot individual samples from both converters.

In [27]:
n_adc1 = arange(1, N*2, 2)
n_adc2 = arange(0, N*2, 2)

plot(n_adc1, x_adc1, 'bo')
plot(n_adc2, x_adc2, 'ro')
x1, x2, y1, y2 = axis()
axis((0, 50, y1, y2));

Combined output.

In [28]:
x = empty(N*2)
x[1::2] = x_adc1
x[::2] = x_adc2

n = arange(0, N*2)

plot(n, x)
grid()
x1, x2, y1, y2 = axis()
axis((0, 100, y1, y2));

Finding statistical differences between samples from ADC1 and ADC2

Fit an ideal sine wave using only data from ADC1, then only data from ADC2 and compare amplitude, offset, frequency and phase from fitted data.

In [31]:
from scipy.optimize import leastsq

def x_model(c, n):
    return c[0] * sin(2. * pi * c[1] * (n + c[2])) + c[3]

def x_error(c, x, n):
    return x_model(c, n) - x

c0 = [ (max(x) - min(x))/2., fg/(2.*fs), -60, mean(x) ]

c = leastsq(x_error, c0, args=(x, n))[0]

c_adc2 = leastsq(x_error, c0, args=(x_adc2, n_adc2))[0]
c_adc1 = leastsq(x_error, c0, args=(x_adc1, n_adc1))[0]

xm = x_model(c, n)
xm_adc1 = x_model(c_adc1, n)
xm_adc2 = x_model(c_adc2, n)

plot(n, xm_adc1 - xm, 'r-')
plot(n_adc1, x_adc1 - xm[1::2], 'ro')
plot(n, xm_adc2 - xm, 'b-')
plot(n_adc2, x_adc2 - xm[::2], 'bo')
x1, x2, y1, y2 = axis()
axis((0, 100, y1, y2))
ylabel("difference from model");

Most of the difference we see here is the 2nd harmonic. Either from the signal generator or from non-linearities in the ADC.

In [32]:
dA_adc1, df_adc1, dph_adc1, doff_adc1 = c_adc1 - c

print "dA_adc1   = ", dA_adc1
print "df_adc1   = ", df_adc1
print "dph_adc1  = ", dph_adc1
print "doff_adc1 = ", doff_adc1
dA_adc1   =  -0.00468228704949
df_adc1   =  2.0756213092e-08
dph_adc1  =  -0.001040428693
doff_adc1 =  0.32756534115
In [33]:
dA_adc2, df_adc2, dph_adc2, doff_adc2 = c_adc2 - c

print "dA_adc2   = ", dA_adc2
print "df_adc2   = ", df_adc2
print "dph_adc2  = ", dph_adc2
print "doff_adc2 = ", doff_adc2
dA_adc2   =  0.00522541980689
df_adc2   =  -2.05564262609e-08
dph_adc2  =  0.00100751010007
doff_adc2 =  -0.327565515905

The most important differences here are offset and phase differences. Offset difference comes from calibration differences between converters. Phase difference comes from inaccurate ADC triggering.

Offset between both ADC is less than 1 digit.

Triggering error is on the order of $10^{-3} \frac{1}{f_s}$

Amplitude accuracy

$U_{ef} = \sqrt{P_g \cdot R}$

$U = \sqrt{2} U_{ef}$

$N = \frac{U}{U_{cc}}(2^{12} - 1)$

$N = \frac{\sqrt{2 \cdot 1\mathrm{mW} \cdot 50\mathrm{\Omega}}}{3.3 \mathrm{V}}(2^{12} - 1) = 392$

In [34]:
print "A = ", c[0]
A =  420.77480581

Frequency accuracy

In [35]:
psd = fft.rfft(x)
plot(abs(psd[1:100]), 'o-');

500 kHz test

$f_g = 500 \mathrm{kHz}$, $P_g = 0 \mathrm{dBm}$

In [36]:
x_adc1, x_adc2 = load_samples("test_500khz.log")

x = empty(N*2)
x[::2] = x_adc1
x[1::2] = x_adc2

plot(x[:50], 'o-');
In [37]:
psd = fft.rfft(x)
plot(abs(psd[1:]), 'o-');
In [ ]: