Commit 1badd3be authored by Christian Schneider's avatar Christian Schneider
Browse files

Merged

parents d2461113 822b9026
......@@ -3,6 +3,11 @@
Created on Tue Apr 9 11:33:36 2019
@author: Oscar
v1.2.2 - OSC:
- inserted wait for WFA completion in the acquire data function
v1.2.1 - OSC:
- implemented connection to multiple boards when more than one is in the
chassis
v1.2.0 - OSC:
- removed 'SW' trigger mode (useless and it wasn't implemented correctly in this library) and Trigger function
- removed 'ChannelUsed' and relative function (it is always asked in the AcquireData function)
......@@ -24,8 +29,9 @@ class ADQ14(object):
import ctypes as ct
import numpy as np
import copy
import time
version='1.2.0'
version='1.2.2'
def __init__(self,board_num = 1):
import os
......@@ -48,7 +54,7 @@ class ADQ14(object):
self.ADQAPI.ADQControlUnit_FindDevices.argtypes = [self.ct.c_void_p]
self.Connect(board_num)
self.Print_adq_device_revisions()
self.__ADCSF = 1.0 #GSamples/s
......@@ -129,11 +135,29 @@ class ADQ14(object):
#---------------------------------
def Connect(self,board_num=1):
class stwrapper(self.ct.Structure):
""" creates a struct to match emxArray_real_T """
_fields_ = [('HWIFType', self.ct.c_int),
('ProductID', self.ct.c_int),
('VendorID', self.ct.c_uint),
('AddressField1', self.ct.c_int),
('AddressField2', self.ct.c_int),
('DevFile', (self.ct.c_char*64)),
('DeviceInterfaceOpened', self.ct.c_uint),
('DeviceSetupCompleted', self.ct.c_uint)]
self._adq = self.ct.c_void_p(self.ADQAPI.CreateADQControlUnit())
self.ADQAPI.ADQControlUnit_FindDevices(self._adq)
DEV_NUM=self.ADQAPI.ADQControlUnit_NofADQ(self._adq)
if DEV_NUM==0:
nof_devices = self.ct.c_int(0)
e = stwrapper()
success = self.ADQAPI.ADQControlUnit_ListDevices(self._adq,self.ct.pointer(e),self.ct.pointer(nof_devices))
nof_devices = nof_devices.value
if nof_devices==0:
print("WARNING: No device has been found")
return 0
......@@ -143,15 +167,23 @@ class ADQ14(object):
print('Error: board_num can\'t be negative!')
return 0
if board_num > DEV_NUM:
print("Erro: Selected board number {}, but only {} boards have been found!".format(board_num,DEV_NUM))
if board_num > nof_devices:
print("Erro: Selected board number {}, but only {} boards have been found!".format(board_num,nof_devices))
return 0
self.devnum = board_num
success = success and self.ADQAPI.ADQControlUnit_OpenDeviceInterface(self._adq,board_num-1)
success = success and self.ADQAPI.ADQControlUnit_SetupDevice(self._adq, board_num-1)
self.devnum = 1
self.id = 'ADQ14' #to implement better
# Print ADQAPI revision
print('ADQAPI loaded, revision {:d}.'.format(self.ADQAPI.ADQAPI_GetRevision()))
rev=self.Print_adq_device_revisions()
if rev<30e3:
print('WARNING: FPGA revision is not in the correct range, are you sure you are using the right board?\n')
self.Clock_ref() #ext ref activated by def
......@@ -183,7 +215,11 @@ class ADQ14(object):
print('Mixed Revision\n')
else :
print('SVN Updated\n')
return revision[0]
def Blink(self):
self.ADQAPI.ADQ_Blink(self._adq,self.devnum)
def Reset(self,Type='COMM'):
'''function reset([Type='COMM']):
......@@ -721,7 +757,7 @@ http://localhost:8988/edit/python_repo/ADQ14.py# tmp = tm
return data_32bit
"""
def Acquire_data(self,channel_mask = 3,conversion_to_volt=True):
def Acquire_data(self,channel_mask = 3,conversion_to_volt=True,timeout=5):
'''This function start the acquisition, it will wait for the trigger.
'''
......@@ -742,6 +778,13 @@ http://localhost:8988/edit/python_repo/ADQ14.py# tmp = tm
if success == 0:
self.__ADQEXC('STARTERR','ADQ_ATDStartWFA failed!')
#Waiting for WFA
start_time = self.time.time()
while(not self.ADQAPI.ADQ_ATDWaitForWFACompletion(self._adq,self.devnum)):
if (self.time.time()-start_time) > timeout:
self.__BASEEXC('TIMEOUT','WFA failed!')
#getting data
#init only what is necessary:
......
......@@ -4,6 +4,11 @@
Created on Wed Sep 6 18:09:39 2017
@author: oscar
v2.5.3 - OSC:
- when loading waves in memory, the channels queue is cleaned. Now after
calling the function load_waves_in_AWG_memory, they will be re-queued
automatically
v2.5.2 - OSC:
- inserted the stop_multiple function
- inserted ext port setup functions
......@@ -462,7 +467,7 @@ class AWGChannel(object):
##------------------------------------------------------------------------------------------ a00 AWG class --------------------------------
class Awgsigd(object):
version = '2.5.2'
version = '2.5.3'
import keysightSD1 as sigd
import numpy as np
import matplotlib.pyplot as plt
......@@ -1028,6 +1033,8 @@ class Awgsigd(object):
self.__check_command( self._awg.waveformLoad(wave,n))
del wave #bugfix
for i in range(4):
self.__load_waves_in_channel(i)
#self.time.sleep(0.1)
def __change_offset_slow(self,channel):
......@@ -1099,8 +1106,6 @@ class Awgsigd(object):
raise ValueError
def setfunc(Channel):
if load_waves_in_memory:
self.load_waves_in_AWG_memory()
self.__check_channel_number(Channel)
tmp = self.channel(Channel)
......@@ -1121,7 +1126,7 @@ class Awgsigd(object):
pars = pars['PORT'],pars['EDGE'],pars['Sync']
port_sn = tmp.ext_port_list.index(pars[0])
if port_sn>0:
port_sn= int(port_sn+3999)
port_sn= int(port_sn+3999) #flags for ext port
self.__check_command(self._awg.AWGtriggerExternalConfig(Channel,port_sn,tmp.trigger_edge_list.index(pars[1]),int(pars[2])))
......
......@@ -212,9 +212,6 @@ class circuit(object):
display_tables((df, df_kappa),
names=['Main Results', 'Linewidths'])
# Plot of data, raw data and (successfully) fitted data if wanted
if plt_res:
cp.plot_cfit(self)
def plot_steps(self):
"""
......@@ -425,14 +422,14 @@ class Reflection(circuit):
self.phi0 = np.arcsin(self.yc_norm / self.r_norm)
# Fit final circle #####################################################
ftol = kwargs['ftol']
maxfev = kwargs['maxfev']
if not kwargs['final_mag']:
self._cir_fit_pars = ft.fit_model_refl(self.freq,
self.circle_norm,
Ql_est, absQc_est, fr_est,
self.phi0,
self._weights,
ftol=ftol)
max_nfev=maxfev)
# Calculate quality factors
self.Ql, self.absQc, self.fr, self.phi1 = \
np.abs(self._cir_fit_pars[0])
......
......@@ -97,7 +97,7 @@ def get_delay(data, comb_slopes=True, f_range=.1):
mean = np.mean(data.phase.y)
SS_tot = np.sum((data.phase.y - mean)**2)
R_sq = 1 - res/SS_tot
else:
lin_fit_first = np.polyfit(data.x[0:fit_range],
data.phase.y[0:fit_range], 1)
......@@ -196,7 +196,7 @@ def fit_model_notch(freq, data, Ql, absQc, fr, phi0, weights, max_nfev=1000):
z1d = np.zeros(data.size * 2, dtype=np.float64)
z1d[0:z1d.size:2] = diff.real ** 2 * weights
z1d[1:z1d.size:2] = diff.imag ** 2 * weights
return z1d.sum()
return z1d
f = spopt.least_squares(res, [np.abs(Ql), np.abs(absQc), fr, phi0],
args=(freq, data),
......@@ -233,8 +233,28 @@ def reflection_model_mag(x, Ql, Qc, x0, c):
return c - (2 * Ql / Qc) ** 2 / (1. + 4 * Ql ** 2 * ((x - x0) / x0) ** 2)
def fit_model_refl(freq, data, Ql, Qc, fr, phi0, weights, ftol=1e-16):
"""Final circle fit of the model to get information about Ql, Qc, fr, phi0
def fit_model_refl(freq, data, Ql, Qc, fr, phi0, weights, max_nfev=1000):
"""Final fit of the model for reflection to get information about Ql,
Qc and fr.
Parameters
-----------
freq : np.array, list
List of frequencies
data : np.array, list
List of complex data points
Ql : float
Guess value for Ql
absQc : float
Guess value for Qc
fr : float
Guess value for resonance frequency
phi0 : float
Guess value for impedance mismatch
weights : np.array, list
Weights for fit.
max_nfev : int
Maximum number of iterations. Increase for increased precision.
"""
def res(params, f, data):
......@@ -245,18 +265,14 @@ def fit_model_refl(freq, data, Ql, Qc, fr, phi0, weights, ftol=1e-16):
z1d[1:z1d.size:2] = diff.imag ** 2 * weights
return z1d
f = spopt.leastsq(res, [Ql, Qc, fr, phi0], args=(freq, data),
full_output=True, xtol=ftol, ftol=ftol)
# f = spopt.least_squares(res, [Ql, Qc, fr, phi0], args=(freq, data),
# bounds=([Ql-.5*Ql, Qc-.5*Qc, fr-10*fr/Ql,
# -np.pi],
# [Ql+0.5*Ql, Qc+0.5*Qc, fr+10*fr/Ql,
# np.pi]),
# xtol=2.3e-16, ftol=2.5e-16, gtol=2.5e-16,
# loss='huber',
# verbose=2)
f = spopt.least_squares(res, [np.abs(Ql), np.abs(Qc), fr, phi0],
args=(freq, data),
verbose=0, max_nfev=max_nfev, xtol=2.3e-16,
ftol=2.3e-16,
bounds=([0, 0, 0, -np.pi],
[10e9, 10e9, 20e9, np.pi]))
return f[0], f[1]
return f.x, f
def fit_mag_refl(freq, data, Ql, Qc, fr, phi0, weights, ftol=1e-16):
......@@ -286,13 +302,21 @@ def subtract_linear_bg(freq, data, fit_range=0.1):
def get_weights(freq, Ql, fr, weight_width):
"""Weighting of the function. In range of FWHM, determined by
(Ql/fr)*weight_width. Outer values are weighted prop. to 1/abs(f-fr)
(Ql/fr)*weight_width. Outer values are weighted prop. to 1/abs(f-fr)^r
"""
width = fr / Ql
weights = np.ones(len(freq))
outer_idx = np.abs(freq - fr) > width * weight_width
weights[outer_idx] = (width * weight_width) / (np.abs(freq[outer_idx] - fr))
left_idx = np.argwhere(outer_idx == 0)[0][0]
right_idx = np.argwhere(outer_idx == 0)[-1][0]
r = 2
weights[right_idx:] = 1 / ((freq[right_idx:] - freq[right_idx]) / (
freq[right_idx] - fr) + 1) ** r
weights[:left_idx] = 1 / ((freq[:left_idx] - freq[left_idx]) / (
freq[left_idx] - fr) + 1) ** r
return weights
......
......@@ -5,6 +5,7 @@ plotting_utilities_module_version = '1.0.1'
@author: David Zoepfl, Christian Schneider
"""
import numpy as np
from scipy.linalg import svd
import pandas as pd
from IPython.display import display, Markdown
try:
......@@ -17,8 +18,12 @@ from collections import OrderedDict
from DataModule.plot_style import cc
from .fit_toolbox import lorentzian_abs, tan_phase, notch_model
from .fit_toolbox import reflection_model, reflection_model_mag
from .fit_toolbox import notch_model_mag
# Bokeh settings
TOOLS = 'box_zoom,pan,wheel_zoom,reset,save'
TOOLTIPS = [('Frequency', '@freq{1.11111111} GHz'), ("Re", "@re"),
("Im", "@im"),
('Mag', '@mag dB'), ('Phase', '@phase°')]
def _get_tools():
tools = ('box_zoom', 'pan', 'wheel_zoom', 'reset', 'save',
......@@ -26,276 +31,404 @@ def _get_tools():
return tools
def plot_rawdata(freq, data, title='-', engine='bokeh'):
"""
Plot complex data in three subplots:
| Im(data) over Re(data) | Mag(data) over freq | Ang(data) over freq |
"""
# Calculate plot values
mag = 20 * np.log10(np.abs(data)) # Power Mag in dB
phase = np.unwrap(np.angle(data)) * 180 / np.pi # Phase in degree
def plot_rawdata(module, engine='b', fig=None, **kwargs):
"""Plot complex data in three subplots:
# Holoviews
if engine[0].lower() == 'h':
pass
| Im(data) over Re(data) | Mag(data) over freq | Ang(data) over freq |
"""
if engine == 'bokeh':
# Calculate data for HoverTool
source_data = ColumnDataSource(
data=dict(
freq=freq,
re=data.real,
im=data.imag,
phase=phase,
mag=mag,
))
tooltips = [('Frequency', '@freq{1.11111111} GHz'), ("Re", "@re"), ("Im", "@im"),
('Mag', '@mag dB'), ('Phase', '@phase°')]
print(title)
# Re over Im
fig1 = figure(title='rawdata', tools=_get_tools())
fig1.xaxis.axis_label = 'Re'
fig1.yaxis.axis_label = 'Im'
fig1.diamond(data.real, data.imag, size=4, fill_color='white')
c1 = fig1.circle('re', 'im', source=source_data, size=3, legend='Data')
# Format nicer HoverTool
hover = HoverTool(renderers=[c1])
fig1.add_tools(hover)
fig1.select(dict(type=HoverTool)).tooltips = OrderedDict(tooltips)
if engine[0].lower() == 'b':
# Real/Imaginary
fig1 = plot_ReIm(module,engine=engine,fit=False, **kwargs)
# Mag over freq
fig2 = figure(tools=_get_tools())
fig2.xaxis.axis_label = 'Frequency (GHz)'
fig2.yaxis.axis_label = 'Magnitude (dB)'
c2 = fig2.line('freq', 'mag', source=source_data, line_width=2)
# Format nicer HoverTool
hover = HoverTool(renderers=[c2])
fig2.add_tools(hover)
fig2.select(dict(type=HoverTool)).tooltips = OrderedDict(tooltips)
fig2 = plot_MagFreq(module, engine=engine, fit=False, **kwargs)
# Phase over freq
fig3 = figure(tools=_get_tools())
fig3.xaxis.axis_label = 'Frequency (GHz)'
fig3.yaxis.axis_label = 'Phase (deg)'
c3 = fig3.line('freq', 'phase', source=source_data, line_width=2)
# Format nicer HoverTool
hover = HoverTool(renderers=[c3])
fig3.add_tools(hover)
fig3.select(dict(type=HoverTool)).tooltips = OrderedDict(tooltips)
fig3 = plot_PhaseFreq(module, engine=engine, fit=False, **kwargs)
fig = gridplot([fig1, fig2, fig3], ncols=3, plot_width=300,
plot_height=300)
show(fig)
elif engine == 'pyplot':
fig = plt.figure(figsize=(12, 4))
fig.subplots_adjust(wspace=0.2)
# Re over Im
elif engine[0].lower() == 'p':
if fig is None:
plt.figure(dpi=200, figsize=(12, 3))
plt.subplots_adjust(wspace=0.33)
# Real/Imaginary
plt.subplot(131)
plt.plot(data.real, data.imag, '.')
plt.title('Im and Re')
plt.xlabel('Re')
plt.ylabel('Im')
plt.grid()
plt.locator_params(axis='x', nbins=4) # Reduce x ticks
# Mag over Freq
plot_ReIm(module, engine=engine, fit=False, **kwargs)
# Mag over freq
plt.subplot(132)
plt.plot(freq, mag)
plt.title('Magnitude (dB)')
plt.xlabel('Frequency (GHz)')
plt.grid()
plt.xlim([freq[0], freq[-1]])
# Phase over Freq
plot_MagFreq(module, engine=engine, fit=False, **kwargs)
# Phase over freq
plt.subplot(133)
plt.plot(freq, phase)
plt.xlabel('Frequency (GHz)')
plt.title('Phase (deg)')
plt.grid()
plt.xlim([freq[0], freq[-1]])
plot_PhaseFreq(module, engine=engine, fit=False, **kwargs)
def plot_cfit(circuit, engine='bokeh', title=''):
def plot_cfit(module, engine='bokeh', fig=None, **kwargs):
"""Plot data and fit.
"""
Plot the data and fit.
if engine[0].lower() == 'b':
# Real/Imaginary
fig1 = plot_ReIm(module, engine=engine,fit=True, **kwargs)
# Mag over freq
fig2 = plot_MagFreq(module, engine=engine, fit=True, **kwargs)
# Phase over freq
fig3 = plot_PhaseFreq(module, engine=engine, fit=True, **kwargs)
# Normalized circle
fig4 = plot_NormCircle(module, engine=engine, fit=True)
fig = gridplot([fig2, fig3, fig1, fig4], ncols=2, plot_height=350,
plot_width=350)
show(fig)
elif engine[0].lower() == 'p':
if fig is None:
plt.figure(dpi=150, figsize=(8.5, 8.5))
plt.subplots_adjust(wspace=0.35, hspace=0.35)
# Magnitude
plt.subplot(221)
plt.title('Magnitude')
plot_MagFreq(module, engine=engine, **kwargs)
# Phase over freq
plt.subplot(222)
plt.title('Phase')
plot_PhaseFreq(module, engine=engine, **kwargs)
# Re/Im
plt.subplot(223)
plt.title('Re/Im')
plot_ReIm(module, engine=engine, **kwargs)
# Normalized circle
plt.subplot(224)
plt.title('Normalized Circle')
plot_NormCircle(module, engine=engine, **kwargs)
def plot_ReIm(module, engine='b', fit=True, **kwargs):
"""Plots imaginary over real part of S parameters
Parameters
-----------
module : data_cplx
Complex datamodule
engine : 'b', 'p'
Engine for plotting. Chose between bokeh and pyplot
fit : bool
Plot fit if available
"""
# Format data
freq = circuit.freq
data = circuit.value_raw
fit_data = circuit.value_calc
z_data_norm = circuit.circle_norm
xc1 = circuit.fitresults_full_model.Value.xc
yc1 = circuit.fitresults_full_model.Value.yc
r = circuit.fitresults_full_model.Value.r
if circuit.type == 'Notch':
offres = 1
# Easy access variable names
freq = module.x
if module.circuit is not None and fit:
# Plot data without electrical delay corrected
data = module.value_raw
else:
offres = -1
# Calculate plot values
# # Data
mag_data = 20 * np.log10(np.abs(circuit.value_raw)) # Power Mag in dB
phase_data = np.unwrap(np.angle(circuit.value), 0.5) * 180 / np.pi # Phase
# # Fit
# # # Correct electric delay
fit_tmp = fit_data * np.exp(2j * np.pi * freq * circuit.delay)
mag_fit = 20 * np.log10(np.abs(fit_tmp)) # Power Mag in dB
phase_fit = np.unwrap(np.angle(fit_tmp), 0.5) * 180 / np.pi # Phase
if engine in ['bokeh', 'b']:
# Calculate data for HoverTool
data = module.value
# Bokeh
if engine[0].lower() == 'b':
# Data
source_data = ColumnDataSource(
data=dict(
freq=freq,
re=data.real,
im=data.imag,
phase=phase_data,
mag=mag_data,
))
source_fit = ColumnDataSource(
data=dict(
freq=freq,
re=fit_data.real,
im=fit_data.imag,
phase=phase_fit,
mag=mag_fit,
mag=20 * np.log10(np.abs(data)),
phase=np.unwrap(np.angle(data), 0.5)* 180/np.pi
))
# Figure
fig = figure(tools=TOOLS)
fig.xaxis.axis_label = 'Re'
fig.yaxis.axis_label = 'Im'
c1 = fig.circle('re', 'im', source=source_data, size=3)
# Format nicer HoverTool
hover = HoverTool(renderers=[c1])
fig.add_tools(hover)
fig.select(dict(type=HoverTool)).tooltips = OrderedDict(TOOLTIPS)
# Fit
if fit and module.circuit is not None:
# Correct electrical delay and calculate mag and phase values
fit_tmp = (module.circuit.value_calc * np.exp(2j * np.pi * freq *
module.circuit.delay))
mag_fit = 20 * np.log10(np.abs(fit_tmp)) # Power Mag in dB
phase_fit = np.unwrap(np.angle(fit_tmp), 0.5) * 180 / np.pi # Phase
source_fit = ColumnDataSource(
data=dict(
freq=freq,
re=module.circuit.value_calc.real,
im=module.circuit.value_calc.imag,
phase=phase_fit,
mag=mag_fit,
))
source_norm = ColumnDataSource(
fig.line('re', 'im', source=source_fit, line_width=2,
color='firebrick')
# Return figure
return fig
elif engine[0].lower() == 'p':
# Plot
plt.plot(data.real, data.imag, '.')
plt.xlabel('Re')
plt.ylabel('Im')
plt.grid()
plt.locator_params(axis='x', nbins=4) # Reduce x ticks
plt.locator_params(axis='y', nbins=4) # Reduce x ticks
# Fit
if fit and module.circuit is not None:
# Correct electrical delay and calculate mag and phase values
fit_tmp = (module.circuit.value_calc * np.exp(2j * np.pi * freq *
module.circuit.delay))
mag_fit = 20 * np.log10(np.abs(fit_tmp)) # Power Mag in dB
phase_fit = np.unwrap(np.angle(fit_tmp), 0.5) * 180 / np.pi # Phase
plt.plot(module.circuit.value_calc.real,
module.circuit.value_calc.imag,
color=cc['r'],
linewidth=1.3)
return None
def plot_MagFreq(module, engine='b', fit=True, **kwargs):
"""Plot Magnitude over frequency"""
# Easy access variable names
freq = module.x
data = module.value
# Bokeh
if engine[0].lower() == 'b':
source_data = ColumnDataSource(
data=dict(
freq=freq,
re=z_data_norm.real,
im=z_data_norm.imag,
phase=phase_data,
mag=mag_data,
re=data.real,
im=data.imag,