Commit 52ce600e authored by Christian Schneider's avatar Christian Schneider
Browse files

Merge branch 'integrated_swept' into resonator

parents 21b1b195 53fcfcdf
import numpy as np import numpy as np
from plotly.subplots import make_subplots
import plotly.graph_objects as go
class Parameter(): class Parameter():
"""Structure to keep the name, data, unit and metadata associated to a Parameter together. """Structure to keep the name, data, unit and metadata associated to a Parameter together.
...@@ -97,7 +99,8 @@ class DataSet(): ...@@ -97,7 +99,8 @@ class DataSet():
# Do it more clever # Do it more clever
self.metadata.update(dataset.metadata) self.metadata.update(dataset.metadata)
""" """
......
...@@ -7,6 +7,9 @@ from collections import ChainMap ...@@ -7,6 +7,9 @@ from collections import ChainMap
import functools import functools
from DataHandling.core import Parameter, Measurement, DataSet from DataHandling.core import Parameter, Measurement, DataSet
import plotly
import plotly.graph_objects as go
class HDF5Saver(h5py.File): class HDF5Saver(h5py.File):
"""Save a Dataset in HDF5 format using h5py. """Save a Dataset in HDF5 format using h5py.
...@@ -29,17 +32,14 @@ class HDF5Saver(h5py.File): ...@@ -29,17 +32,14 @@ class HDF5Saver(h5py.File):
super().__init__(fname, mode='w-', libver='latest') super().__init__(fname, mode='w-', libver='latest')
self.swmr_mode = True # Allow to read the file when it is being written. self.swmr_mode = True # Allow to read the file when it is being written.
print([[parameter.name, parameter.data] for parameter in dataset.parameters.values()])
self.parameters = self.create_group('Parameters') self.parameters = self.create_group('Parameters')
for parameter in dataset.parameters.values(): for parameter in dataset.parameters.values():
parameter_data_set = self.parameters.create_dataset(parameter.name, parameter_data_set = self.parameters.create_dataset(parameter.name,
data=parameter.data) data=parameter.data)
parameter_data_set.attrs['Unit'] = parameter.unit
for key, value in parameter.metadata.items(): for key, value in parameter.metadata.items():
parameter_data_set.attrs[key] = value parameter_data_set.attrs[key] = value
parameter_data_set.attrs['Unit'] = parameter.unit
self.shape = {parameter.name: len(parameter.data) for parameter in dataset.parameters.values()} self.shape = {parameter.name: len(parameter.data) for parameter in dataset.parameters.values()}
...@@ -63,9 +63,9 @@ class HDF5Saver(h5py.File): ...@@ -63,9 +63,9 @@ class HDF5Saver(h5py.File):
for i, parameter in enumerate(measurement.indexing): for i, parameter in enumerate(measurement.indexing):
measurement_data_set.dims[i].label = parameter measurement_data_set.dims[i].label = parameter
for key, value in dataset.metadata.items(): for key, value in dataset.metadata.items():
self.attrs[key] = value self.attrs[key] = value
print(key, value)
self.flush() self.flush()
...@@ -79,6 +79,22 @@ class HDF5Saver(h5py.File): ...@@ -79,6 +79,22 @@ class HDF5Saver(h5py.File):
def load_hdf5(fname): def load_hdf5(fname):
return HDF5Loader(fname).dataset return HDF5Loader(fname).dataset
def plot_hdf5(fname, swept_parameter, fixed_parameter, measurement):
dataset = load_hdf5(fname)
fig = go.Figure()
#for i, parameter in enumerate(dataset.measurements[measurement].indexing):
for i, value in enumerate(dataset.parameters[swept_parameter].data):
y = dataset.measurements[measurement].data[i]
x = dataset.parameters[fixed_parameter].data
fig.add_trace(go.Scatter(x = x, y = 20*np.log10(np.abs(y)),mode='lines', name=dataset.parameters[swept_parameter].name + str(value), line = dict(width = 2)))
fig.update_layout(plot_bgcolor='white')
fig.update_xaxes(showline = True, linewidth=2, linecolor='black',
showgrid=True, gridwidth=1, gridcolor='Black', title_text = 'f, GHz')
fig.update_yaxes(showline = True, linewidth=2, linecolor='black',
showgrid=True, gridwidth=1, gridcolor='Black', title_text = '{}'.format(measurement))
fig.show()
class HDF5Loader(h5py.File): class HDF5Loader(h5py.File):
"""Load an HDF5 file to a Dataset """Load an HDF5 file to a Dataset
......
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Created on Fri Mar 28 09:43:05 2014 @author: Seyed Iman Mirzaei, Oscar Gargiulo, Christian Schneider, David Zoepfl, Romain Albert
@author: Seyed Iman Mirzaei, Oscar Gargiulo, Christian Schneider, David Zoepfl Driver for ENA E5071C and
Driver for ENA E5071C """
v2.2.0 - OSC:
- changed all units to SI
v2.1.1 - CHR:
- Changed to binary data format (to be able to use pyvisa)
v2.0.0 - CHR: import pyvisa as visa
- Adapted to Driver/Instrument structure
v2.0.1 - OSC:
- migrated to VISA
"""
import visa
import time import time
import numpy as np import numpy as np
from tqdm import tqdm_notebook from tqdm import tqdm_notebook
import struct import struct
from Instruments.Drivers.Keysight.VNA_Keysight import VNAKeysight
# Versoin # Versoin
version = '2.2.0' version = '3.0.0'
print('E5071C {}'.format(version)) print('E5071C {}'.format(version))
class E5071C(object): class E5071C(VNAKeysight):
global version global version
def __init__(self, ip, *pars, **kwargs): def __init__(self, ip, *pars, **kwargs):
rm = visa.ResourceManager('@py') rm = visa.ResourceManager('@py')
self._inst = rm.open_resource('TCPIP::{}::INSTR'.format(ip)) self._inst = rm.open_resource('TCPIP::{}::INSTR'.format(ip))
self.version = version
self.ip = ip self.ip = ip
# Set timeout to a minute # Set timeout to a minute
...@@ -44,9 +34,8 @@ class E5071C(object): ...@@ -44,9 +34,8 @@ class E5071C(object):
else: else:
self._inst.timeout = 60*1000 # Set to a minute self._inst.timeout = 60*1000 # Set to a minute
print(self._inst.timeout) super().__init__()
self.average()
def com(self, command, arg="?", raw=False): def com(self, command, arg="?", raw=False):
"""Function to communicate with the device. Gives the current status """Function to communicate with the device. Gives the current status
if no arg is given if no arg is given
...@@ -89,6 +78,10 @@ class E5071C(object): ...@@ -89,6 +78,10 @@ class E5071C(object):
self._inst.write("{} {}".format(command, arg)) self._inst.write("{} {}".format(command, arg))
return 0 return 0
def close(self):
'''Close connection to the instrument'''
self._inst.close()
def get_complex_value(self, command): def get_complex_value(self, command):
"""Function to communicate with the device. Gives the current status """Function to communicate with the device. Gives the current status
if no arg is given if no arg is given
...@@ -102,73 +95,16 @@ class E5071C(object): ...@@ -102,73 +95,16 @@ class E5071C(object):
self._inst.write(':form:bord norm') self._inst.write(':form:bord norm')
return resp return resp
def identify(self):
"""Returns Identification String of the device"""
return self.com('*OPT')
def close(self):
'''Close connection to the instrument'''
self._inst.close()
def output(self, arg='?'):
"""Turns RF output power on/off
Give no argument to query current status.
Parameters
-----------
arg : int, str
Set state to 'ON', 'OFF', 1, 0
"""
return self.com(":OUTP", arg)
def power(self, power='?', channel=''):
"""Set or read current power"""
return float(self.com(":SOUR{}:POW:LEV:IMM:AMPL".format(channel), power))
def average(self, number_average=1): def average(self, number_average=1):
"""Set/reset all parameters for doing an average measurement""" """Set/reset all parameters for doing an average measurement"""
self.number_average = number_average super().average(number_average)
if number_average == 1: if number_average == 1:
self.average_state(0)
self.com(':TRIG:SEQ:AVER', 'OFF') self.com(':TRIG:SEQ:AVER', 'OFF')
else: else:
self.average_state(1)
self.average_count(number_average)
self.com(':TRIG:SEQ:AVER', 'ON') self.com(':TRIG:SEQ:AVER', 'ON')
def average_reset(self, channel=''):
"""Reset averages"""
return self.com(":SENS{}:AVER:CLE".format(channel), "")
def average_count(self, count='?', channel=''):
"""Set/query number of averages"""
return int(self.com(":SENS{}:AVER:COUN".format(channel), count))
def average_state(self, state='?', channel=''):
"""Sets/query averaging state"""
return self.com(":SENS{}:AVER:STAT".format(channel), state)
def freq_start(self, freq='?', channel=''):
"""Set/query start frequency"""
return float(self.com(":SENS{}:FREQ:STAR".format(channel), freq))
def freq_stop(self, freq='?', channel=''):
"""Set/query stop frequency"""
return float(self.com(":SENS{}:FREQ:STOP".format(channel), freq))
def freq_center(self, freq='?', channel=''):
"""Set/query center frequency in Hz"""
return float(self.com(":SENS{}:FREQ:CENT".format(channel), freq))
def freq_span(self, freq='?', channel=''):
"""Set/query span in Hz"""
return float(self.com(":SENS{}:FREQ:SPAN".format(channel), freq))
def freq_npoints(self, points='?', channel=''):
"""Set/Query number of points"""
return int(self.com(":SENS{}:SWE:POIN".format(channel), points))
def freq_semgents(self, segments, BW=100, power=-50): def freq_semgents(self, segments, BW=100, power=-50):
"""Defined the VNA frequency through segments. """Defined the VNA frequency through segments.
...@@ -202,14 +138,6 @@ class E5071C(object): ...@@ -202,14 +138,6 @@ class E5071C(object):
self.com(':SENS:SWE:TYPE', 'SEGM') self.com(':SENS:SWE:TYPE', 'SEGM')
self.com(':SENS:SEGM:ARB', 1) # Allow arb. segments (reversed, etc) self.com(':SENS:SEGM:ARB', 1) # Allow arb. segments (reversed, etc)
self.com(':SENS:SEGM:DATA', segment_str) self.com(':SENS:SEGM:DATA', segment_str)
def IFBW(self, BW='?', channel=''):
"""Set/query IF Bandwidth for specified channel"""
return self.com(":SENS{}:BAND:RES".format(channel), BW)
def sweep_type(self, type='?', channel=''):
"""Set/query the type of sweep. Must be either linear, logartihmic, segmented or power."""
return self.com(":SENS{}:SWE:TYPE".format(channel), type)
def Spar(self, Par='?', trace=1): def Spar(self, Par='?', trace=1):
if type(Par) != str: if type(Par) != str:
...@@ -223,13 +151,13 @@ class E5071C(object): ...@@ -223,13 +151,13 @@ class E5071C(object):
else: else:
raise Exception('''S parameter must be either 'S11', 'S12', 'S21' or 'S22'.''') raise Exception('''S parameter must be either 'S11', 'S12', 'S21' or 'S22'.''')
def traces_number(self, num='1', channel='1'):
"""Set number of traces"""
return int(self.com("CALC{}:PAR:COUN".format(channel), num))
def trace_select(self, num=1, channel='1'): def trace_select(self, num=1, channel='1'):
"""Select trace number num""" """Select trace number num"""
self.com('CALC{}:PAR{}:SEL'.format(channel, num), '') self.com('CALC{}:PAR{}:SEL'.format(channel, num), '')
def set_electrical_delay(self, delay=0., num=1, channel=''):
""" Set the electrical delay of the trace. Notice that a trace should be already selected."""
self.com("CALC{}:SEL:CORR:EDEL:TIME".format(channel), delay)
def Format(self, Format='?', Trace=1): def Format(self, Format='?', Trace=1):
"""Set Data Format """Set Data Format
...@@ -247,7 +175,7 @@ class E5071C(object): ...@@ -247,7 +175,7 @@ class E5071C(object):
| 'PPH': Positive phase | 'PPH': Positive phase
| '' (def): the format will be queried | '' (def): the format will be queried
""" """
self.com("CALC:SEL:CORR:EDEL:TIME", 0)
return self.com('CALC1:SEL:FORM', Format) return self.com('CALC1:SEL:FORM', Format)
# READING data # READING data
...@@ -266,71 +194,6 @@ class E5071C(object): ...@@ -266,71 +194,6 @@ class E5071C(object):
"""Return the last raw S measurement (before correction) in complex form.""" """Return the last raw S measurement (before correction) in complex form."""
return self.get_complex_value(':CALC:TRACe{}:DATA:SDATa?'.format(trace)) return self.get_complex_value(':CALC:TRACe{}:DATA:SDATa?'.format(trace))
def read_corrected_measurement(self, spar='S21'):
"""Return the last calibrated S measurement (after correction) in complex form."""
data = self.com(':SENSe:DATA:CORRdata', '? {}'.format(spar), raw=True)
return np.asarray(data[0::2], dtype=np.float) +1.j*np.asarray(data[1::2], dtype=np.float)
def read_settings(self):
"""Returns current state of VNA parameters as dict
Frequency in GHz.
"""
freq_start = self.freq_start()
freq_stop = self.freq_stop()
freq_span = freq_stop - freq_start
freq_npoints = self.freq_npoints()
BW = self.IFBW()
Spar = self.Spar()
format_meas = self.Format()
power = self.power()
output = self.output()
avg = self.average_count()
par = {'f_start (Hz)': freq_start,
'f_stop (Hz)': freq_stop,
'f_start (GHz)': freq_start * 1e-9,
'f_stop (GHz)': freq_stop * 1e-9,
'IF - BW (Hz)': BW,
'S_parameter ': Spar,
'Format': format_meas,
'Span (Hz)': freq_span,
'Points': freq_npoints,
'power (dBm)': power,
'output': output,
'averages': avg,
'averaging': self.average_state(),
'correction': self.correction()}
return par
def set_settings(self, **kwargs):
com_dict = {
'f_start (Hz)': self.freq_start,
'f_stop (Hz)': self.freq_stop,
'IF - BW (Hz)': self.IFBW,
'S_parameter ': self.Spar,
'Format': self.Format,
'Points': self.freq_npoints,
'power (dBm)': self.power,
'averaging': self.average_state,
'averages': self.average_count
}
for k in kwargs.keys():
try:
com_dict[k](kwargs[k])
except KeyError:
pass
def correction(self, state='?'):
"""Query or set correction status (to ON/OFF)
Parameters
-----------
state : '?', 'ON', 'OFF', 1, 0
State of correction. Use nothing or '?' for query
"""
return self.com('SENS:CORR:STAT', state)
def current_correction(self): def current_correction(self):
"""Return the current type correction used.""" """Return the current type correction used."""
...@@ -346,6 +209,13 @@ class E5071C(object): ...@@ -346,6 +209,13 @@ class E5071C(object):
self.com(':TRIG:AVER', 'OFF') self.com(':TRIG:AVER', 'OFF')
self.com(':TRIG:SEQ:SOUR', 'INT') self.com(':TRIG:SEQ:SOUR', 'INT')
def send_trigger(self):
""" Send the trigger to initiate the measurement. """
self.com(':TRIG:SEQ:SINGLE', '')
def start_and_wait_end_measurement(self): def start_and_wait_end_measurement(self):
"""Start the measurement and wait up to the end of the measurment. """Start the measurement and wait up to the end of the measurment.
......
This diff is collapsed.
This diff is collapsed.
...@@ -56,6 +56,7 @@ class BondedVNAFrequency(): ...@@ -56,6 +56,7 @@ class BondedVNAFrequency():
self._stop = None self._stop = None
self._npoints = None self._npoints = None
self._type = None self._type = None
self.unit = 'Hz'
if data is None: if data is None:
self.start = start self.start = start
...@@ -300,9 +301,12 @@ class VNA(Instrument): ...@@ -300,9 +301,12 @@ class VNA(Instrument):
self.devtype = 'VNA' self.devtype = 'VNA'
# Load driver ########################################################## # Load driver ##########################################################
if id_string == 'VNA3' or id_string[:4] == "VNA4": if id_string == 'VNA3':
from .Drivers.Keysight.E5080 import E5080 from .Drivers.Keysight.E5080 import E5080
self.driver = E5080 self.driver = E5080
elif id_string[:4] == "VNA4":
from .Drivers.Keysight.P5003A import P5003A
self.driver = P5003A
elif id_string == 'VNA1' or id_string == 'VNA2': elif id_string == 'VNA1' or id_string == 'VNA2':
from .Drivers.Keysight.E5071C import E5071C from .Drivers.Keysight.E5071C import E5071C
self.driver = E5071C self.driver = E5071C
...@@ -326,6 +330,8 @@ class VNA(Instrument): ...@@ -326,6 +330,8 @@ class VNA(Instrument):
## We need to keep track of them to be able to store them in the metadata of the measurement ## We need to keep track of them to be able to store them in the metadata of the measurement
self.segments = None self.segments = None
self.S_parameters = None
def start(self): def start(self):
self.instr.set_trigger() self.instr.set_trigger()
...@@ -372,7 +378,8 @@ class VNA(Instrument): ...@@ -372,7 +378,8 @@ class VNA(Instrument):
S_parameters = ['S21'], S_parameters = ['S21'],
correction = False, correction = False,
ignore_security = False, ignore_security = False,
format_vna = 'MLOG'): format_vna = 'MLOG',
synchrone = True):
""" """
Measure the S scattering matrix and return a DataSet with the result (complex number). Measure the S scattering matrix and return a DataSet with the result (complex number).
...@@ -398,46 +405,78 @@ class VNA(Instrument): ...@@ -398,46 +405,78 @@ class VNA(Instrument):
| 'IMAG' imaginary part of the complex data | 'IMAG' imaginary part of the complex data
| 'UPH': Extended phase | 'UPH': Extended phase
| 'PPH': Positive phase | 'PPH': Positive phase
synchrone: bool
If True, this method will perform the measurement and wait for the end of the measurement.
In that case, this method will send back the dataset with the result.
If False, this method only start the measurement and the data will need to be retrieve
using the get_data methods later.
""" """
S_parameters = [spar.upper() for spar in S_parameters] if before_measurement:
## We define all the important parameter before the first measurement
if correction:
self.instr.correction('ON')
else:
self.instr.correction('OFF')
if not ignore_security:
calibration = self.instr.current_correction()
if correction and calibration[0] == "SOLT2":
raise Exception('The correction is activated and will send power to port 2. If '\
'it is volontary, please put ignore_security to True.')
for spar in S_parameters:
if spar in ['S12', 'S22']:
raise Exception('One of the S parameter you want to measure will send power '\
'to port 2. If it is volontary, please put ignore_security to True.')
number_trace = len(S_parameters) self.S_parameters = [spar.upper() for spar in S_parameters]
self.instr.traces_number(number_trace)
for i, spar in enumerate(S_parameters): if correction:
self.instr.trace_select(i+1) self.instr.correction('ON')
self.instr.Spar(spar, i+1) else:
self.instr.Format(format_vna) self.instr.correction('OFF')
if not ignore_security:
calibration = self.instr.current_correction()
if correction and calibration[0] == "SOLT2":
raise Exception('The correction is activated and will send power to port 2. If '\
'it is volontary, please put ignore_security to True.')
for spar in S_parameters:
if spar in ['S12', 'S22']:
raise Exception('One of the S parameter you want to measure will send power '\
'to port 2. If it is volontary, please put ignore_security to True.')
number_trace = len(S_parameters)
self.instr.traces_number(number_trace)
for i, spar in enumerate(S_parameters):
self.instr.trace_select(i+1)
self.instr.Spar(spar, i+1)
self.instr.Format(format_vna)
self.set_electrical_delay(0, i+1)
if before_measurement:
return DataSet({'VNA Frequency': Parameter('VNA Frequency', np.asarray(self.get_frequencies(), dtype='float'), 'Hz')}, return DataSet({'VNA Frequency': Parameter('VNA Frequency', np.asarray(self.get_frequencies(), dtype='float'), 'Hz')},
{spar : Measurement(spar, np.empty((1), dtype=np.complex128), '', ['VNA Frequency']) for spar in S_parameters}, {spar : Measurement(spar, np.empty((1), dtype=np.complex128), '', ['VNA Frequency']) for spar in self.S_parameters},
{'Correction VNA': correction, 'Segments': str(self.segments)}) {'Correction VNA': correction, 'Segments': str(self.segments)})
else: else:
self.instr.start_and_wait_end_measurement() if synchrone:
self.instr.start()
self.instr.wait_end_measurement()
err = self.instr.get_error()
if err != 'No error':
raise Exception('VNA error.')
return DataSet(measurements = {spar : Measurement(spar,
self.instr.read_measurement(trace=i+1),
'',
['VNA Frequency'])
for i, spar in enumerate(self.S_parameters)})
else:
self.instr.start()
def get_data(self):