forked from analogdevicesinc/pyadi-iio
/
ad4630.py
251 lines (210 loc) · 8.27 KB
/
ad4630.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
# --------------------LICENSE AGREEMENT----------------------------------------
# Copyright (c) 2020 Analog Devices, Inc. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# - Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# - Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# - Modified versions of the software must be conspicuously marked as such.
# - This software is licensed solely and exclusively for use with
# processors/products manufactured by or for Analog Devices, Inc.
# - This software may not be combined or merged with other code in any manner
# that would cause the software to become subject to terms and conditions
# which differ from those listed here.
# - Neither the name of Analog Devices, Inc. nor the names of its
# contributors may be used to endorse or promote products derived from this
# software without specific prior written permission.
# - The use of this software may or may not infringe the patent rights of
# one or more patent holders. This license does not release you from the
# requirement that you obtain separate licenses from these patent holders
# to use this software.
import sys
try:
import iio
except:
print ("iio not found!")
sys.exit(0)
import time
import numpy as np
import matplotlib.pyplot as plt
num_samples = 65536 ###Captures 65536 sample from the AD4630
N = num_samples
channel_no = 0 ##AD4630 has two channels
## Modes Register Bit Description
# OUT_DATA_MD: 0 = 24-bit data
# 1 = 16-bit data, 8-bit CM
# 2 = 24-bit data, 8-bit CM
# 3 = 30-bit avg data, 1 OR bit, 1 SYNC bit (Additional config needed)
# 4 = 32-bit test pattern
##
# DDR_MD: 0 = SDR Mode
# 1 = DDR Mode
##
# CLK_MD: 0 = SPI clocking mode.
# 1 = echo clock mode.
# 2 = master clock mode.
# 3 = invalid setting
##
# LANE_MD: 0 = one lane per channel.
# 1 = two lanes per channel.
# 2 = four lanes per channel.
# 3 = Unsupported
###
OUT_DATA_MD = 2
DDR_MD = 0
CLK_MD = 0
LANE_MD = 2
if (OUT_DATA_MD == 1 or OUT_DATA_MD == 2):
##Enable capture of VCOM data
VCOM_ENABLE = 1
else:
VCOM_ENABLE = 0
if (OUT_DATA_MD == 0 or OUT_DATA_MD == 2):
diff_bits = 24 # 24-bit data
elif (OUT_DATA_MD == 1):
diff_bits = 16 # 16-bit data
elif (OUT_DATA_MD == 3):
diff_bits = 30 # 30-bit data
else:
diff_bits = 32 # 32-bit data
# Setup Context and set the IP addres1
my_ip = 'ip:10.126.203.135' # default hardcoded ip for remote access
try:
ctx = iio.Context(my_ip)
print("Successfully connected to the Data Storm DAQ board")
except:
print("Unable to connect to the Data Storm DAQ board.")
print("Please check the IP address of the host Ethernet Adapter")
sys.exit(0)
rxadc = ctx.find_device("ad4630") # RX/ADC Core in HDL for DMA
v3 = rxadc.find_channel("voltage0") ##There are 2-channels for AD4630
v3.enabled = True ##Enable the channel in IIO to start receiving data
time.sleep(10) ##A 10s delay is needed.
###Reads back from the Register address 0x0A
reg_val = hex(rxadc.reg_read(0x06))
print("Revision register = " + reg_val)
## Write mode_val to modes register 0x20
mode_val = (LANE_MD << 6) | (CLK_MD << 4) | (DDR_MD << 3) | OUT_DATA_MD
rxadc.reg_write(0x20, mode_val)
reg_val = hex(rxadc.reg_read(0x20))
print("Modes register = " + reg_val)
### Gets the sampling frequency for AD7380###
fs_str = rxadc.attrs["sampling_frequency"].value
fs = int(fs_str)
print("Sampling_Frequency = " , fs)
## Read Common Mode Voltage if flag is set ##
vcom_val = v3.attrs["common_mode_voltage"].value
if (VCOM_ENABLE):
print("Common Mode Voltage =", vcom_val)
###Implement a buffer to capture data from the DMA engine#######
rxbuf = iio.Buffer(rxadc, num_samples, False) # False = non-cyclic buffer
for j in range(5): #Flush buffers.
rxbuf.refill()
x = rxbuf.read()
# got our data, hence delete the buffer
del rxbuf
####Extract data from buffer########
data = np.frombuffer(x, np.int64)
### Format the data according to modes set
if (channel_no == 0):
if (OUT_DATA_MD == 0 or OUT_DATA_MD == 2):
data = (data & 0xffffffff) >> 8
elif (OUT_DATA_MD == 1):
if (CLK_MD == 2):
if (LANE_MD == 2):
data = (data & 0xffffffff) >> 8
else:
data = (data & 0xffffffff) >> 16
elif (OUT_DATA_MD == 3):
data = (data & 0xffffffff) >> 2
else:
data = data & 0xffffffff
else:
if (OUT_DATA_MD == 0 or OUT_DATA_MD == 2):
data = ((data >> 32) & 0xffffffff) >> 8
elif (OUT_DATA_MD == 1):
if (CLK_MD == 2):
if (LANE_MD == 2):
data = ((data >> 32) & 0xffffffff) >> 8
else:
data = ((data >> 32) & 0xffffffff) >> 16
elif (OUT_DATA_MD == 3):
data = ((data >> 32) & 0xffffffff) >> 2
else:
data = (data >> 32) & 0xffffffff
###Convert the extracted 2's complement data into positive/negative values####
for i in range(0, len(data)):
if(data[i] > 2**(diff_bits - 1)):
data[i] = data[i] - 2**diff_bits
else:
data[i] = data[i]
data_dc = round(np.average(data)) ##Gets the DC value present in the captured signal
adc_amplitude_adj = 2.0**(diff_bits-1) ##x**y is same as x^y in python
max_data = max(data)
min_data = min(data)
adc_amplitude_peak = max_data - min_data
mag_adj = (adc_amplitude_peak/(2*adc_amplitude_adj))
#print(mag_adj)
mag_adj_db = 20*np.log10(mag_adj)
data_no_dc = [0]*num_samples
######Removing DC content to avoid spectral leakage#########
for i in range(0, N):
data_no_dc[i]= data[i] - data_dc
######### Apply 7-term Blackman-Harris Window 180dB to remove spectral leakage caused by non-coherent sampling#######
a0 = 0.27105140069342
a1 = 0.43329793923448
a2 = 0.21812299954311
a3 = 0.06592544638803
a4 = 0.01081174209837
a5 = 0.00077658482522
a6 = 0.00001388721735
t = np.linspace(0, 1, num_samples, False)
norm = 1
win = a0 - a1*np.cos(2*np.pi* t) + a2*np.cos(4*np.pi* t) - a3*np.cos(6*np.pi* t)+ a4*np.cos(8*np.pi*t) - a5*np.cos(10*np.pi*t) + a6*np.cos(12*np.pi*t)
win = win*norm
######Compute the FFT of the window########
win_fft = np.fft.fft(win, N*10)/(N) ####Same as MATLAB command to increase the FFT resolution to see lobes
win_fft_mag = 2*(np.abs(win_fft[0:int(N/2)+1]))
win_fft_mag_db = 20*np.log10(win_fft_mag[1:int(N/2)+1])
#######Find the maximum value and shift everything down#####
win_fft_mag_db_max = max(win_fft_mag_db)
for i in range(0, len(win_fft_mag_db)):
win_fft_mag_db[i] = win_fft_mag_db[i] - win_fft_mag_db_max
######Apply the Window to the captured Data###################
windowed_data = data_no_dc*win
#windowed_data = data_no_dc ###In case of coherent sampling no need to apply window
win_data_fft = np.fft.fft(windowed_data)/(N) # FFT normalized
win_data_fft_mag = 2*(np.abs(win_data_fft[0:int(N/2)+1]))
global freq_domain_magnitude_db
freq_domain_magnitude_db = 20*np.log10((win_data_fft_mag[1:int(N/2)+1])/adc_amplitude_adj)
freq_domain_mag_db_loss = mag_adj_db - max(freq_domain_magnitude_db) ###Amplitude loss due to windowing application
for i in range(0, len(freq_domain_magnitude_db)):
freq_domain_magnitude_db[i] = freq_domain_magnitude_db[i] + freq_domain_mag_db_loss
freq_array = np.linspace(0, 0.5*fs, int(N/2), False)
len_freq_array = len(freq_array)
freq_array = freq_array[5:len_freq_array] ###Discard the DC component/bins from the FFT
freq_domain_magnitude_db = freq_domain_magnitude_db[5:len_freq_array]
###Begin Plotting########
plt.figure(1)
plt.subplot(2, 1, 1)
plt.plot(data_no_dc)
#plt.plot(data)
plt.title('ADC Data captured from AD4630')
plt.xlabel('Time Samples')
plt.ylabel('Amplitude')
plt.subplot(2,1,2)
plt.title("Frequency Domain Plot")
plt.xlabel("Frequency (Hz)")
plt.ylabel("Magnitude (dB)")
plt.plot(freq_array, freq_domain_magnitude_db)
plt.tight_layout()
plt.show()
## Write back default driver mode_val to modes register
rxadc.reg_write(0x20, 0x82)
reg_val = hex(rxadc.reg_read(0x20))
print("Modes register = " + reg_val)
del ctx
del rxadc