External_Adjustment_for_the_Atmel_iADC - david-macmahon/wiki_convert_test GitHub Wiki
- External Adjustment for the Atmel iADC - January 2011 (Hong Chen, Mark Wagner)
- AT84AD001B_datasheet
- AT84AD001C_iADC-datasheet
- Memo on iadc_controller by David George
- Download: Model file and bof file
- Download (Code): Iadc_simulation_code.tar
- ''' Code: adc_simulation.m '''
Software simulation of interleaved ADC for effects caused by gain mismatch, delay and offset. Simulate with simple monochromatic sin wave.
% Interleaved ADC mismatches simulation
% including gain, delay, and offset;
% Sampling frequency
Fs = 2e9;
% Sample time
T = 1/Fs;
% Length of signal
L = (2^16)*8;
% Time vector
t = (0:L-1)*T;
t1 = t(1:2:end);
t2 = t(2:2:end);
% Frequency of Sine Wave
freq = 1e8;
% delay
% default = 0.0
delay=0.05;
t2=t2+delay*(1/Fs);
% gain
% default= 1.0
gain=1.01;
% offset
% default = 0.0
offset=0.05;
% Create a sine wave of input frequency.
x = sin(2*pi*t*freq);
x1 = (sin(2*pi*t1*freq)+offset)*gain;
x2 = sin (2*pi*t2*freq);
x(1:2:end)=x1;
x(2:2:end)=x2;
nfft = 2^nextpow2(L); % Next power of 2 from length of y
y = fft(x,nfft)/L;
f = Fs/2*linspace(0,1,nfft/2+1);
f = f/(1e6)*1.0;
% Plot single-sided amplitude spectrum.
figure(3)
semilogy(f,2*abs(y(1:nfft/2+1)))
graph_title=strcat('Power Spectrum of a',char(32));
graph_title2=num2str(freq/(1e6)*1.0);
graph_title2=strcat(graph_title2,'MHz Sine Wave, with delay:');
graph_title2=strcat(graph_title2,num2str(delay,8));
graph_title2=strcat(graph_title2,' offset:');
graph_title2=strcat(graph_title2,num2str(offset,8));
graph_title2=strcat(graph_title2,' gain:');
graph_title2=strcat(graph_title2,num2str(gain,8));
graph_title={graph_title,graph_title2};
title(graph_title);
xlabel('Frequency (MHz)');
ylabel('Power');
% Loop through different frequency value
% figure(2)
% for i=1:1:15,
% freq=freq+i*(freq/15);
% x1 = (sin(2*pi*t1*freq)+offset)*gain;
% x2 = sin (2*pi*t2*freq);
% x(1:2:end)=x1;
% x(2:2:end)=x2;
% y = fft(x,nfft)/L;
% semilogy(f,2*abs(y(1:nfft/2+1)))
% graph_title=strcat('Power Spectrum of a ',num2str(freq));
% graph_title=strcat(graph_title,'Hz Sine Wave, with delay:');
% graph_title=strcat(graph_title,num2str(delay,8));
% graph_title=strcat(graph_title,' offset:');
% graph_title=strcat(graph_title,num2str(offset,8));
% graph_title=strcat(graph_title,' gain:');
% graph_title=strcat(graph_title,num2str(gain,8));
% title(graph_title);
% xlabel('Frequency (Hz)');
% ylabel('Power');
% pause(1);
% end
- Download (Code and Plots):Iadc_simulation.tar
- Download (Code): Iadc_ext_adj_code.tar
- iadc.py
A collection of functions that can be used to adjust the iADC by writing to the registers.
'''
Functions to write to registers of iADC and adjust it
Author: Hong Chen
Date: July 23, 2010
'''
val=roach.read('iadc_controller',128)
'''
read the original value of iadc_controller
'''
def read_iadc():
val=roach.read('iadc_controller',128)
return val
'''
#first thing to do
'''
def start_test():
print '\n'
print 'Starting the test...'
roach.blindwrite('iadc_controller','%c%c%c%c'%(0x0,0x0,0x03,0x0))
time.sleep(0.001)
print 'You can write to the registers now.'
return read_iadc()
'''
# reset the DCM
'''
def reset_dcm():
#print 'resetting the dcm...'
roach.blindwrite('iadc_controller','%c%c%c%c'%(0x0,0x0,0x03,0x03))
time.sleep(0.001)
#print 'resetting dcm completed'
return read_iadc()
'''
start a new calibration phase
Old DATA =0110 0100 0110 1100
DATA =X X X X 1 1 0 X X X X X X X X X = 0110 1100 0110 1100 = 0x6c, 0x6c
ADDR =000
'''
def new_cal():
print '\n'
print 'starting a new calibration phase...'
roach.blindwrite('iadc_controller','%c%c%c%c'%(0x6c,0x6c,0x0,0x1),offset=0x4)
time.sleep(0.001) # probably unnecessary wait for delay to take
reset_dcm()
print 'new calibration phase completed.'
return read_iadc()
'''
set to no calibration mode
so that gain compensation and offset compensation can be done
'''
def no_calibration():
print '\n'
print 'setting to no calibration mode...'
roach.blindwrite('iadc_controller','%c%c%c%c'%(0x60,0x6c,0x0,0x01),offset=0x4)
time.sleep(0.001) # probably unnecessary wait for delay to take
reset_dcm()
print 'no calibration mode setting completed.'
return read_iadc()
'''
#intput_i
analog input channel selection: analog I-i/q
xxxx|xx0x|xx10|xxxx =0x00,0x20 (adc0_data)
#or = 0110 0100 0110 1100 = 0x64, 0x6c
'''
def input_i():
print '\n'
print 'selecting input channel I...'
roach.blindwrite('iadc_controller','%c%c%c%c'%(0x64,0x6c,0x0,0x1),offset=0x4)
time.sleep(0.001) # probably unnecessary wait for delay to take
reset_dcm()
print 'Input channel: I-> ADC I&Q'
return read_iadc()
'''
#intput_q
analog input channel selection: analog Q-i/q
xxxx|xx0x|xx0x|xxxx =0x00,0x20 (adc0_data)
#or = 0110 0100 0100 1100 = 0x64, 0x4c
'''
def input_q():
print '\n'
print 'selecting input channel Q...'
roach.blindwrite('iadc_controller','%c%c%c%c'%(0x64,0x4c,0x0,0x1),offset=0x4)
time.sleep(0.001) # probably unnecessary wait for delay to take
reset_dcm()
print 'Input channel:Q-> ADC I&Q'
return read_iadc()
'''
# input_iq
analog input channel selection: analog I-i, Q-q
xxxx|xx0x|xx11|xxxx
0110 0100 0111 1100 = 0x64,0x7c
'''
def input_iq():
print '\n'
print 'selecting input channel: I & Q ...'
roach.blindwrite('iadc_controller','%c%c%c%c'%(0x64,0x7c,0x0,0x1),offset=0x4)
time.sleep(0.001) # probably unnecessary wait for delay to take
reset_dcm()
print 'Input channel: I-> ADC I; Q-> ADC Q'
return read_iadc()
# default offset value = 00000000b = 0x00 = 0LSB
# i channel and q channel
offset_vi=0x00
offset_vq=0x00
'''
# offset compensation
#step 1 * 0.25LSB
address 010 = 0x02
DATA7 to DATA0: channel I
DATA15 to DATA8: channel Q
code 11111111b=0xff = 31.75LSB
code 10000000b=00000000b= 0x80=0x00= 0LSB
code 01111111b=0x7f= -31.75LSB
# code 10000001b=0x81 = 1LSB
'''
def offset_inc(channel):
global offset_vi
global offset_vq
v=offset_vi
v2=offset_vq
if v>=128:
v=v+1
elif v==0:
v=129
else:
v=v-1
if v2>=128:
v2=v2+1
elif v2==0:
v2=129
else:
v2=v2-1
if channel=='i':
offset_vi=v
elif channel=='q':
offset_vq=v2
elif channel=='iq' or channel=='qi':
offset_vi=v
offset_vq=v2
else:
print 'invalid argument!'
return
roach.blindwrite('iadc_controller','%c%c%c%c'%(offset_vq,offset_vi,0x02,0x01),offset=0x4)
time.sleep(0.001) # probably unnecessary wait for delay to take
return read_iadc()
'''
# offset compensation
#step -1 * 0.25LSB
address 010 = 0x02
DATA7 to DATA0: channel I
DATA15 to DATA8: channel Q
code 11111111b=0xff = 31.75LSB
code 10000000b=00000000b= 0x80=0x00= 0LSB
code 01111111b=0x7f= -31.75LSB
# code 00000001b=0x01 = -1LSB
'''
def offset_dec(channel):
global offset_vi
global offset_vq
v=offset_vi
v2=offset_vq
if v==128:
v=1
elif v==0:
v=1
elif v<128:
v=v+1
else:
v=v-1
if v2==128:
v2=1
elif v2==0:
v2=1
elif v2<128:
v2=v2+1
else:
v2=v2-1
if channel=='i':
offset_vi=v
elif channel=='q':
offset_vq=v2
elif channel=='iq' or channel=='qi':
offset_vi=v
offset_vq=v2
else:
print 'invalid argument!'
return
roach.blindwrite('iadc_controller','%c%c%c%c'%(offset_vq,offset_vi,0x02,0x01),offset=0x4)
time.sleep(0.001) # probably unnecessary wait for delay to take
reset_dcm()
return read_iadc()
'''
# offset compensation
#step -1 * 0.25LSB
address 010 = 0x02
DATA7 to DATA0: channel I
DATA15 to DATA8: channel Q
code 11111111b=0xff = 31.75LSB
code 10000000b=00000000b= 0x80=0x00= 0LSB
code 01111111b=0x7f= -31.75LSB
# code 00000000b=0x00 = 0LSB
'''
def offset_0(channel):
print '\n'
print 'setting the offset to 0 for channel: '+channel
global offset_vi
global offset_vq
if channel=='i':
offset_vi=0
elif channel=='q':
offset_vq=0
elif channel=='iq' or channel=='qi':
offset_vi=0
offset_vq=0
roach.blindwrite('iadc_controller','%c%c%c%c'%(offset_vi,offset_vq,0x02,0x01),offset=0x4)
time.sleep(0.001) # probably unnecessary wait for delay to take
reset_dcm()
print 'setting completed. channel '+channel+': offset 0'
return read_iadc()
'''
# offset compensation inc loop
'''
def offset_inc_loop(channel,n):
for i in range(0,n):
offset_inc(channel)
return read_iadc()
'''
# offset compensation dec loop
'''
def offset_dec_loop(channel,n):
for i in range(0,n):
offset_dec(channel)
return read_iadc()
'''
# offset compensation
#step -1 * 0.25LSB
address 010 = 0x02
DATA7 to DATA0: channel I
DATA15 to DATA8: channel Q
code 11111111b=0xff = 31.75LSB
code 10000000b=00000000b= 0x80=0x00= 0LSB
code 01111111b=0x7f= -31.75LSB
# code 11111111b=0xff = 31.75LSB
'''
def offset_max():
global offset_vi,offset_vq
print '\n'
print 'setting offset to maximum value...'
roach.blindwrite('iadc_controller','%c%c%c%c'%(0xff,0xff,0x02,0x01),offset=0x4)
time.sleep(0.001) # probably unnecessary wait for delay to take
reset_dcm()
offset_vi=0xff
offset_vq=0xff
print 'setting completed. Offset: 31.75LSB(maximum)'
return read_iadc()
'''
# offset compensation
#step -1 * 0.25LSB
address 010 = 0x02
DATA7 to DATA0: channel I
DATA15 to DATA8: channel Q
code 11111111b=0xff = 31.75LSB
code 10000000b=00000000b= 0x80=0x00= 0LSB
code 01111111b=0x7f= -31.75LSB
# code 01111111b=0xff = -31.75LSB
'''
def offset_min():
print '\n'
print 'setting the offset to minimum value...'
global offset_vi,offset_vq
roach.blindwrite('iadc_controller','%c%c%c%c'%(0x7f,0x7f,0x02,0x01),offset=0x4)
time.sleep(0.001) # probably unnecessary wait for delay to take
reset_dcm()
offset_vi=0x7f
offset_vq=0x7f
print 'setting completed. Offset: -31.75LSB(minimum)'
return read_iadc()
gain_vi=0x80
gain_vq=0x80
'''
# analog gain adjustment
# gain set to minimum = -1.5dB
# address = 001 = 0x01
DATA7 to DATA0: channel I
DATA15 to DATA8: channel Q
code 00000000=0x00= -1.5dB
code 10000000 = 0x80 = 0dB
code 11111111=0xff = 1.5dB
'''
def gain_min():
print '\n'
print 'setting the gain to minimum value...'
global gain_vi,gain_vq
roach.blindwrite('iadc_controller','%c%c%c%c'%(0x00,0x00,0x01,0x1),offset=0x4)
time.sleep(0.001) # probably unnecessary wait for delay to take
reset_dcm()
gain_vi=0x00
gain_vq=0x00
print 'setting completed. Gain: -1.5dB(minimum)'
return read_iadc()
'''
# analog gain adjustment
# gain set to 0dB
# address = 001 = 0x01
DATA7 to DATA0: channel I
DATA15 to DATA8: channel Q
code 00000000=0x00= -1.5dB
code 10000000 = 0x80 = 0dB
code 11111111=0xff = 1.5dB
'''
def gain_0():
print '\n'
print 'setting the gain to 0dB...'
global gain_vi,gain_vq
roach.blindwrite('iadc_controller','%c%c%c%c'%(0x80,0x80,0x01,0x1),offset=0x4)
time.sleep(0.001) # probably unnecessary wait for delay to take
reset_dcm()
gain_vi=0x80
gain_vq=0x80
print 'setting completed. Gain: 0dB'
return read_iadc()
'''
# analog gain adjustment
#gain set to max=1.5dB
# step -1*0.011 dB
# address = 001 = 0x01
DATA7 to DATA0: channel I
DATA15 to DATA8: channel Q
code 00000000=0x00= -1.5dB
code 10000000 = 0x80 = 0dB
code 11111111=0xff = 1.5dB
# code 01111111 = 0x7f = -0.001dB
'''
def gain_max():
print '\n'
print 'setting the gain to maximum value...'
global gain_vi,gain_vq
roach.blindwrite('iadc_controller','%c%c%c%c'%(0xff,0xff,0x01,0x1),offset=0x4)
time.sleep(0.001) # probably unnecessary wait for delay to take
reset_dcm()
gain_vi=0xff
gain_vq=0xff
print 'setting completed. Gain: 1.5dB(maximum)'
return read_iadc()
'''
analog gain adjustment on channel i
'''
def gain_inc_loop_i(n):
global gain_vi,gain_vq
v=gain_vi
if (n+v>255):
return 'too big!'
result=arange(0,n,1)
for i in range(0,n):
v=v+1
roach.blindwrite('iadc_controller','%c%c%c%c'%(gain_vq,v,0x01,0x1),offset=0x4)
time.sleep(0.001) # probably unnecessary wait for delay to take
reset_dcm()
gain_vi=v
return read_iadc()
gc_v=0x00
'''
Gain Compensation adjustment
Gain compensation
NOTE: ONLY 7 BITS, THE EXAMPLE GIVEN IN THE DATASHEET IS NOT REALLY CORRECT
Data6 to Data0: channel I/Q (Q is matched to I for interleaving adjustment)
Code 11111111b: ?.315 dB
Code 10000000b: 0 dB
Code 0000000b: 0 dB
Code 0111111b: 0.315 dB
Steps: 0.005 dB
Data6: sign bit
Data15 to Data7 = XXX
'''
'''
increase gain compensation by 1
'''
def gc_inc():
global gc_v
v=gc_v
if v==64:
v=1
elif v>64:
v=v-1
elif v==63:
print 'maximum reached!'
return
else:
v=v+1
roach.blindwrite('iadc_controller','%c%c%c%c'%(v,v,0x03,0x01),offset=0x4)
time.sleep(0.001) # probably unnecessary wait for delay to take
reset_dcm()
gc_v=v
return read_iadc()
'''
decrease gain compensation by 1
'''
def gc_dec():
global gc_v
v=gc_v
if v==0:
v=65
elif v>=64:
v=v+1
elif v==127:
print 'minimum reached!'
return
else:
v=v-1
roach.blindwrite('iadc_controller','%c%c%c%c'%(v,v,0x03,0x01),offset=0x4)
time.sleep(0.001) # probably unnecessary wait for delay to take
reset_dcm()
gc_v=v
return read_iadc()
'''
gain compensation adjustment, using gc_inc(), loop
'''
def gc_inc_loop(n):
for i in range(0,n):
gc_inc()
'''
gain compensation adjustment, using gc_dec(), loop
'''
def gc_dec_loop(n):
for i in range(0,n):
gc_dec()
'''
gain compensation to minimum 11111111 =0xff, -0.315db
'''
def gc_min():
print '\n'
print 'setting the gain compensation to minimum value...'
global gc_v
roach.blindwrite('iadc_controller','%c%c%c%c'%(0xff,0xff,0x03,0x01),offset=0x4)
time.sleep(0.001) # probably unnecessary wait for delay to take
reset_dcm()
gc_v=0xff
print 'setting completed. Gain compensation: -0.315dB(minimum)'
return read_iadc()
'''
gain compensation to maximum 01111111 =0x7f, 0.315db
REAL VALUE SHOULD BE 00111111 =0x3f, 0.315db
'''
def gc_max():
print '\n'
print 'setting the gain compensation to maximum value...'
global gc_v
roach.blindwrite('iadc_controller','%c%c%c%c'%(0x3f,0x3f,0x03,0x01),offset=0x4)
time.sleep(0.001) # probably unnecessary wait for delay to take
reset_dcm()
gc_v=0x7f
print 'setting completed. Gain compensation: 0.315dB'
return read_iadc()
'''
gain compensation to 0 00000000 = 0x00, 0db
'''
def gc_0():
print '\n'
print 'setting the gain compensation to 0...'
global gc_v
roach.blindwrite('iadc_controller','%c%c%c%c'%(0x00,0x00,0x03,0x01),offset=0x4)
time.sleep(0.001) # probably unnecessary wait for delay to take
reset_dcm()
gc_v=0x0
print 'setting completed. Gain compensation: 0dB'
return read_iadc()
fisda_v=0 # default value
'''
adjust the fine sampling data adjustment (FISDA) on channel Q
ADDR = 111 = 0x07
DATA10 to DATA6
'''
def fisda_inc():
global fisda_v,drda_i,drda_q
if fisda_v==0xf:
print 'maximum reached!'
return
elif fisda_v==16:
fisda_v=1
elif fisda_v>16:
fisda_v=fisda_v-1
else:
fisda_v=fisda_v+1
b=((fisda_v&0x3)<<6)+(drda_q<<3)+drda_i # marks out the lowest 2 bits in fisda_v, together with drda_q & drda_i, DATA7 to DATA0
a=fisda_v>>2
roach.blindwrite('iadc_controller','%c%c%c%c'%(a,b,0x07,0x01),offset=0x4)
time.sleep(0.001) # probably unnecessary wait for delay to take
reset_dcm()
return read_iadc()
'''
adjust the fine sampling data adjustment (FISDA) on channel Q
ADDR = 111 = 0x07
DATA10 to DATA6
'''
def fisda_dec():
global fisda_v,drda_i,drda_q
if fisda_v==0x18:
print 'minimum reached!'
return
elif fisda_v==0:
fisda_v=0x11 # assume 11111 is the minimum, -60 ps, so 10001 should be -4 ps
elif fisda_v>16:
fisda_v=fisda_v+1
else:
fisda_v=fisda_v-1
b=((fisda_v&0x3)<<6)+(drda_q<<3)+drda_i # marks out the lowest 2 bits in fisda_v, together with drda_q & drda_i, DATA7 to DATA0
a=fisda_v>>2
##print '%x %x'%(a,b)
roach.blindwrite('iadc_controller','%c%c%c%c'%(a,b,0x07,0x01),offset=0x4)
time.sleep(0.001) # probably unnecessary wait for delay to take
reset_dcm()
return read_iadc()
'''
adjust the fine sampling data adjustment (FISDA) on channel Q
ADDR = 111 = 0x07
DATA10 to DATA6
loop using fisda_inc()
'''
def fisda_inc_loop(n):
for i in range(0,n):
fisda_inc()
return read_iadc()
'''
adjust the fine sampling data adjustment (FISDA) on channel Q
ADDR = 111 = 0x07
DATA10 to DATA6
loop using fisda_dec()
'''
def fisda_dec_loop(n):
for i in range(0,n):
fisda_dec()
return read_iadc()
- analysis_function.py
A collection of functions that can be used to process data.
'''
functions to deal with data
'''
'''
% Author: Hong Chen
% Date: August 5,2010
'''
import corr,pylab,numpy,struct
'''
input: stream x which represents a sine wave
output: the amplitude of the sine wave
calculate the amplitude of x
using the RMS = amp/sqrt(2)
'''
def amp(x):
rms=0
for i in range(0,size(x)):
rms=rms+x[i]*x[i]
rms=rms/size(x)
rms=sqrt(rms)
amp=rms*sqrt(2)
return amp
'''
input: two stream x1 and x2, each of them represents a sine wave
output: the difference of the amplitudes of x1 and x2
take in two streams of data, caculate their amplitudes seperately, and get the difference between the two amplitudes
'''
def diff_amp(x1,x2):
a1=amp(x1)
a2=amp(x2)
print 'amp1 '+str(a1)+' '+'amp2 '+str(a2)
return a1-a2
'''
input: a stream x
output: the difference of the amplitudes of x1 and x2, where x1 and x2 are from x (every other sample)
'''
def diff_amp_x(x):
x1=x[0:size(x)+1:2]
x2=x[1:size(x)+1:2]
return diff_amp(x1,x2)
'''
input: a stream x, the smapling rate
output: the frequency of x
'''
def tone_freq(data, fs):
data_len = data.size
fft_data = numpy.fft.fft(data)
fft_abs = numpy.abs(fft_data[0:data_len/2]);
max_pos = numpy.where(fft_abs == fft_abs.max())[0][0]
return float(max_pos)*float(fs)/float(data_len)
'''
input: a stream f which is the result of fft of a sine wave, the smapling rate
output: the frequency of x
'''
def get_freq(f, fs):
data_len = size(f)*2
max_pos = numpy.where(f == f.max())[0][0]
return float(max_pos)*float(fs)/float(data_len)
'''
input: a stream f which is the result of fft of a sine wave, the sampling frequency Fs
output: the height of spike resulted in interleaving
'''
def h_interleaving(f,Fs):
freq=get_freq(f,Fs)
ind=int((Fs/2-freq)*size(f)/(Fs/2))
print 'ind '+str(ind)+' '+str(size(f))+' freq: '+str(freq)+' Fs:'+str(Fs)
print ind*1.0/size(f), ((Fs/2)-freq)/(Fs/2)
start=ind-50
if start<0:
start=0
end=ind+50
if end>size(f):
end=size(f)
spike=f.max()/100
position=-1
for i in range(start,end):
if f[i]>spike:
spike=f[i]
position=i
print 'spike: '+str(spike)+' position: '+str(position)
return position
'''
find the height of spike which is resulted in interleaving issue
'''
def h_s(f,position):
spike=f[position]
print 'spike: '+str(spike)+' position: '+str(position)
return spike
- config_setup.py
Connect to ROACH board and run the design.
'''
Connect to the ROACH board and run the design
'''
'''
% Author: Hong Chen
% Date: August 5,2010
'''
import corr,time
roach=corr.katcp_wrapper.FpgaClient('192.168.1.217',7147)
time.sleep(0.001) # wait for roach to connect
roach.progdev('interleaved_adc_64_2010_Jul_06_1446.bof')
- start.py
Test initialization. start_test() defined in iadc.py
'''
% Author: Hong Chen
% Date: August 5,2010
'''
print '-------------------------------------'
print 'Programming the bof file...'
print '-------------------------------------'
roach.progdev('interleaved_adc_64_2010_Jul_06_1446.bof')
# autamatically do the first thing
start_test()
- maker.py
Trigger the snap64 block and start capturing data. Then read the data out and we'll get 2^16 64-bit unsigned numbers. Each of these unsigned numbers is from 8 8-big unsigned numbers concatenated together. This program will split them up and restore them back to their original value before writing them to a data file.
'''
% Author: Hong Chen
% Date: August 5,2010
'''
import corr
import struct
import time
from numpy import *
from pylab import *
'''
Trigger the snap64 block and capture
data, then read it and get (2**16)
64-bit unsigned numbers. Each of the
unsigned numbers is concatenated by 8
8-bit unsigned numbers. We need to
split each long number and get the
original data.
After the 8*(2**16) 8-bit signed number
is recovered, write it to a file using
argument 'name' as file name.
'''
def datafile_maker(name):
mask0=2**8-1
mask1=mask0<<8
mask2=mask1<<8
mask3=mask2<<8
divisor=float(2**7)
roach.write_int('snap64_ctrl',0)
roach.write_int('snap64_ctrl',7)
time.sleep(0.1)
roach.write_int('snap64_ctrl',0)
x = roach.read('snap64_bram_msb', 65536*4)
x1 = roach.read('snap64_bram_lsb',65536*4)
y0 = struct.unpack('>65536l', x)
y1 = struct.unpack('>65536l',x1)
# interleave part I
y=arange(0,size(y0)*8,1)
for i in range(0,size(y0)):
y[8*i] = short((y0[i] & mask3) >> 24)
y[8*i+1] = short((y0[i] & mask2) >> 16)
y[8*i+2] = short((y0[i] & mask1) >> 8)
y[8*i+3] = short((y0[i] & mask0))
# interleave part II
for i in range(0,size(y0)):
y[8*i+4] = short((y1[i] & mask3) >> 24)
y[8*i+5] = short((y1[i] & mask2) >> 16)
y[8*i+6] = short((y1[i] & mask1) >> 8)
y[8*i+7] = short((y1[i] & mask0))
# convert to signed integer: 8 bit
for i in range(0,size(y)):
if (y[i]>127):
y[i]=y[i]-256
# write to file
datafile=open(name,'w')
for i in range(0,size(y)):
datafile.write(str(y[i])+'\n')
datafile.close()
'''
i = range(0,size(y))
print size(y)
plot(i[0:1024/10],y[0:1024/10])
show()
'''
'''
plot(y[1:100])
show()
'''
'''
# FFT and plot
# adc freq=1 GHz = 1000 MHz
Fs = 2000000000 # sampling frequency: 2 GHz
T = 1.0/Fs # sample time
L = 8*(2**16) # length of sample points
nfft = L
k = fft(y,nfft)/L
f = Fs/2*linspace(0.0,1.0,nfft/2+1)
#print size(f)
#print size(k)
semilogy(f,2*abs(k[0:nfft/2+1]))
title('interleaved ADCs on roach')
xlabel('frequency')
ylabel('magnitude')
show()
'''
# val=roach.read('iadc_controller',128)
- '' fft_plot.m''
This program will read the data file made by maker.py, do Fourier Transform to the data and plot its spectrum in frequency domain.
% read from a datafile and do the fft, then plot it
%
% Author: Hong Chen
% Date: August 5,2010
clear all;
x= dlmread('filename.txt','\n'); % read from datafile
Fs=2e9; % set the sampling rate
L=length(x);
nfft=2^nextpow2(L);
f=Fs/2*linspace(0,1,nfft/2+1);
Y3=fft(x,nfft)/L;
semilogy(f,2*abs(Y3(1:nfft/2+1))) % plot the result
graph_title='interleaved adcs with input signal: 93MHz (test1)';
title(graph_title);
xlabel('Frequency (Hz)');
ylabel('Power');
- zoom.m
% plot the a small part of the sample data
% zoom in and see the details
%
% Author: Hong Chen
% Date: August 5,2010
x= dlmread('default_0.txt','\n'); % read from a data file
start=1; % set the begin of the segment
length=100; % set the length of the segment
p=1:2:length; % the x-axis reference
plot(p,x(start:2:start+length-1),'or')
hold on;
plot(p+1,x(start+1:2:start+length),'o')
hold on;
plot(x(start:1:start+length-1),'-k')
- tester.py
Basically the same as maker.py, but return the data as a long array instead of writing it into a data file.
'''
% Author: Hong Chen
% Date: August 5,2010
'''
import corr
import struct
import time
from numpy import *
from pylab import *
'''
basically the same as maker.py
but return the raw data as a long array rather
than write to a data file
no arguments required
'''
def tester():
mask0=2**8-1
mask1=mask0<<8
mask2=mask1<<8
mask3=mask2<<8
divisor=float(2**7)
roach.write_int('snap64_ctrl',0)
roach.write_int('snap64_ctrl',7)
time.sleep(0.1)
roach.write_int('snap64_ctrl',0)
x = roach.read('snap64_bram_msb', 65536*4)
x1 = roach.read('snap64_bram_lsb',65536*4)
y0 = struct.unpack('>65536l', x)
y1 = struct.unpack('>65536l',x1)
# interleave part I
y=arange(0,size(y0)*8,1)
for i in range(0,size(y0)):
y[8*i] = short((y0[i] & mask3) >> 24)
y[8*i+1] = short((y0[i] & mask2) >> 16)
y[8*i+2] = short((y0[i] & mask1) >> 8)
y[8*i+3] = short((y0[i] & mask0))
# interleave part II
for i in range(0,size(y0)):
y[8*i+4] = short((y1[i] & mask3) >> 24)
y[8*i+5] = short((y1[i] & mask2) >> 16)
y[8*i+6] = short((y1[i] & mask1) >> 8)
y[8*i+7] = short((y1[i] & mask0))
# convert to signed integer: 8 bit
for i in range(0,size(y)):
if (y[i]>127):
y[i]=y[i]-256
'''
# write to file
datafile=open(name,'w')
for i in range(0,size(y)):
datafile.write(str(y[i])+'\n')
datafile.close()
'''
return y
'''
i = range(0,size(y))
print size(y)
plot(i[0:1024/10],y[0:1024/10])
show()
'''
'''
plot(y[1:100])
show()
'''
# FFT and plot
# adc freq=1 GHz = 1000 MHz
Fs = 2000000000 # sampling frequency: 2 GHz
T = 1.0/Fs # sample time
L = 8*(2**16) # length of sample points
nfft = L
k = fft(y,nfft)/L
f = Fs/2*linspace(0.0,1.0,nfft/2+1)
#print size(f)
#print size(k)
semilogy(f,2*abs(k[0:nfft/2+1]))
title('interleaved ADCs on roach')
xlabel('frequency')
ylabel('magnitude')
show()
#'''
# val=roach.read('iadc_controller',128)
- fft_out.py
One step further than maker.py. This program extracts data out of the design, performs Fourier Transform, plots the spectrum and returns the frequency array as output value. The argument "channel" allows value 0,1 or 2 to select signals from either of the two interleaved ADCs or from both of them. (I encountered problems of missing spikes in spectrum plot using python, so I have to switch to Matlab to do the spectrum plotting, as in #fft_plot.m)
'''
% Author: Hong Chen
% Date: August 5,2010
'''
import corr
import struct
import time
from numpy import *
from pylab import *
def fft_out(channel):
mask0=2**8-1
mask1=mask0<<8
mask2=mask1<<8
mask3=mask2<<8
divisor=float(2**7)
roach.write_int('snap64_ctrl',0)
roach.write_int('snap64_ctrl',7)
time.sleep(0.1)
roach.write_int('snap64_ctrl',0)
x = roach.read('snap64_bram_msb', 65536*4)
x1 = roach.read('snap64_bram_lsb',65536*4)
y0 = struct.unpack('>65536l', x)
y1 = struct.unpack('>65536l',x1)
# interleave part I
y=arange(0,size(y0)*8,1)
for i in range(0,size(y0)):
y[8*i] = short((y0[i] & mask3) >> 24)
y[8*i+1] = short((y0[i] & mask2) >> 16)
y[8*i+2] = short((y0[i] & mask1) >> 8)
y[8*i+3] = short((y0[i] & mask0))
# interleave part II
for i in range(0,size(y0)):
y[8*i+4] = short((y1[i] & mask3) >> 24)
y[8*i+5] = short((y1[i] & mask2) >> 16)
y[8*i+6] = short((y1[i] & mask1) >> 8)
y[8*i+7] = short((y1[i] & mask0))
# convert to signed integer: 8 bit
for i in range(0,size(y)):
if (y[i]>127):
y[i]=y[i]-256
'''
i = range(0,size(y))
print size(y)
plot(i[0:1024/10],y[0:1024/10])
show()
'''
'''
plot(y[1:100])
show()
'''
# FFT and plot
# adc freq=1 GHz = 1000 MHz
Fs = 1000000000 # sampling frequency: 2 GHz
T = 1.0/Fs # sample time
L = 4*(2**16) # length of sample points
nfft = L
if channel==0:
p = y[0:L:2]
elif channel==1:
p = y[1:L:2]
elif channel==2:
p = y
Fs=Fs*2
T=1.0/Fs
L=L*2
nfft=L
else:
print 'invalid argument(s)!'
return
k = fft(p,nfft)/L
f = Fs/2*linspace(0.0,1.0,nfft/2+1)
return 2*abs(k[0:nfft/2+1])
'''
#print size(f)
#print size(k)
semilogy(f,2*abs(k[0:nfft/2+1]))
title('interleaved ADCs on roach')
xlabel('frequency')
ylabel('magnitude')
show()
'''
# val=roach.read('iadc_controller',128)
- auto_adjust.py
Running this program will automatically adjust the offset, gain and delay mismatch.
'''
Using e2v Dual 8-bit 1Gsps ADC, AT84AD001B, automatically adjust the register to minimize the offset, gain and delay mismatch
Should be working on AT84AD001C as well
Author: Hong Chen
Date: July 23, 2010
'''
Fs=2e9
execfile('maker.py')
execfile('tester.py')
execfile('fft_out.py')
execfile('iadc.py')
execfile('analysis_function.py')
execfile('start.py') # reprogram the bof file
no_calibration() # set to no calibration mode, so that I'll be able to access the registers and adjust the gain and offset compensation
offset_0('iq') # set offset to 0 for both channel I and channel Q
gain_0() # set analog gain to 0 dB (channel I & channel Q)
gc_0() # set gain compensation to 0 dB
test=tester() # get the 8*(2**16) sample points, main purpose here is to make sure that the program has been compiled successfully
# if an error message pops out, please re-run this *auto_adjust.py* again.
'''
automatically adjust offset for channel I or channel Q (seperately)
TOL is just an approximate expectation to the final result, when it says 'unseccessful!', the result may still be good
as long as it's not too far away from TOL
# function fft_out(chnl) defined in analysis_function.py
# function offset_inc(channel), offset_dec(channel) defined in iadc.py
related material in datasheet: AT84AD004B,p35 AT84AD001C,p33
'''
def offset_adjust(channel):
print '\n Start adjusting the offset for channel: '+channel
if channel=='i':
chnl=1
else:
chnl=0
Y=fft_out(chnl) # get 8*(2**16) sample points, and do the fft, then get the absolute values and set them to Y
off_v0=Y[0] # measure the height(power) of the spike at 0Hz
print off_v0 # print the current value in the register which controls the offset
off_v1=off_v0
TOL=Y.max()/600 # define the tolerance
print 'TOL '+str(TOL)
while off_v0>TOL: # try increasing the offset, see if it can reduce the offset error
offset_inc(channel)
Y=fft_out(chnl)
off_v1=Y[0]
print 'offset value: '+str(off_v1)
if off_v1<off_v0:
off_v0=off_v1
else: # if increase the offset will not improve the performance, restore to the last state
offset_dec(channel)
break
if off_v0>TOL: # if the error is still larger than the tolerance
Y=fft_out(chnl)
off_v0=Y[0]
while off_v0>TOL: # try decreasing the offset, set if it can reduce the offset error
offset_dec(channel)
Y=fft_out(chnl)
off_v1=Y[0]
print 'offset value '+ str(off_v1)
if off_v1<off_v0:
off_v0=off_v1
else: # if decrease the offset will not improve the performance, restore it to the last state, which should be the optimal
offset_inc(channel)
break
if off_v0>TOL: # print the message, showing if we can reduce the offset error to the tolerance, and shows the value off the two registers
print 'unseccessful! offset_vi: '+str(offset_vi)+' offset_vq: '+str(offset_vq)+'\n'
else:
print 'secessful! offset_vi: '+str(offset_vi)+' offset_vq: '+str(offset_vq)+'\n'
'''
automatically adjust the gain mismatch between two channels
TOL2 is just an approximate expectation to the final result, when it says 'unseccessful!', the result may still be good
as long as it's not too far away from TOL2
# function amp(x) defined in analysis_function.py
# function gc_inc_loop(n), gc_dec_loop(n) defined in iadc.py
related material in datasheet: AT84AD004B,p35 AT84AD001C,p33
'''
def gain_adjust():
print '\n start adjusting the gain mismatch...'
test=tester()
x1=test[0:size(test)+1:2] # channel Q
x2=test[1:size(test)+1:2] # channel I
amp1=amp(x1) # measure the amplitude of the sine wave x1, function amp() defined in "analysis_function.py"
amp2=amp(x2) # measure the amplitude of the sine wave x2
print 'amp1 '+str(amp1)+' '+'amp2 '+str(amp2)
TOL2=abs(amp1-amp2)/2000 # define the tolerance
print 'TOL: '+str(TOL2)
diff0=amp1-amp2 # calculate the difference of the amplitudes for the two streams
while abs(diff0)>TOL2: # try to reduce the difference till the tolerance
if diff0<0:
gc_dec_loop(1)
test=tester()
diff1=diff_amp_x(test)
if abs(diff1)<abs(diff0):
diff0=diff1
else:
gc_inc_loop(1)
break;
else:
gc_inc_loop(1)
test=tester()
diff1=diff_amp_x(test)
if abs(diff1)<abs(diff0):
diff0=diff1
else:
gc_dec_loop(1)
break;
if abs(diff0)>TOL2: # output message, gain difference, value of the register controls gain compensation
print 'unseccessful! gain difference: '+str(diff0)+' gc_v: '+str(gc_v)+'\n'
else:
print 'yeah! gain difference: '+str(diff0)+' gc_v:' +str(gc_v)+'\n'
'''
delay adjustment
TOL is just an approximate expectation to the final result, when it says 'unseccessful!', the result may still be good
as long as it's not too far away from TOL
# function h_interleaving(f,Fs), h_s(f,position) defined in analysis_function.py
# function fft_out(2) defined in fft_out.py
# function fisda_inc(),fisda_dec() defined in iadc.py
related material in datasheet: AT84AD004B,p36 AT84AD001C,p34
'''
def delay_adjust():
print '\n start adjusting the delay...'
f=fft_out(2) # get the result of fft of the sample data
position=h_interleaving(f,Fs) # find the position of the spike mainly resulted in interleaving issue
spike=h_s(f,position) # measure the height(power) of the spike mainly resulted in interleaving issue
TOL=f.max()/200 # define the tolerance
while spike>TOL:
fisda_inc() # fisda: Fine Sampling Delay Adjustment(FISDA) on channel Q
f=fft_out(2)
spike2=h_s(f,position)
if spike2>spike:
fisda_dec()
break
else:
spike=spike2
while spike>TOL:
fisda_dec()
f=fft_out(2)
spike2=h_s(f,position)
if spike2>spike:
fisda_inc()
break
else:
spike=spike2
if spike>TOL:
print 'unseccessful! '+str(spike)+' '+str(f.max())+' '+'fisda_v(hex): '+'%x'%fisda_v+'\n'
else:
print 'seccessful! '+str(spike)+' '+str(f.max())+' '+'fisda_v(hex): '+'%x'%fisda_v+'\n'
'''
Using the functions defined above, adjujst the offset for channel i and q, and then adjust the gain mismatch, finnally adjust the delay mismatch
'''
datafile_maker('default1.txt')
offset_adjust('i')
offset_adjust('q')
gain_adjust()
delay_adjust()
print 'Automatic adjustment completed. Now start capturing data and make a datafile...'
datafile_maker('test1.txt') # function datafile_maker(filename) defined in maker.py
# capture data and put the data into test1.txt
print 'Now datafile test1.txt is ready.'
- Download: Supplement Notes (Code, data files, and plots)
- Code: anag_gain_test.py
The Analog gain adjustment is not used as the step is too big for our purpose. But during our test we found some discrepancy between what the datasheet states and the actual behavior. This program perform some very rough test, but it's enough to show the inconsistent result. This observation is described in section 7.2 in the memo.
'''
analog gain adjustment on channel i
Author: Hong Chen
Date: July 23, 2010
'''
def gain_inc_loop_i(n):
global gain_vi,gain_vq
v=gain_vi
if (n+v>255):
return 'too big!'
result=arange(0,n,1)
for i in range(0,n):
v=v+1
roach.blindwrite('iadc_controller','%c%c%c%c'%(gain_vq,v,0x01,0x1),offset=0x4)
time.sleep(0.001) # probably unnecessary wait for delay to take
reset_dcm()
y=tester()
result[i]=y.max()
datafile=open('analog_gain_test_result.txt','w')
datafile.write(str(gain_vi)+'\n')
for i in range(0,n):
datafile.write(str(gain_vi+i)+' '+str(result[i])+'\n')
datafile.close()
gain_vi=v
datafile=open('analog_gain_test_plot.txt','w')
for i in range(0,n):
datafile.write(str(result[i])+'\n')
datafile.close()
return read_iadc()
gain_vi=128 # set to minimum value
gain_inc_loop_i(100) # this program is quite slow due to the frequent register setting and file writing