S5048 Network Analyzer driver:
Saba-Mehshar opened this issue · 3 comments
This is my Qcodes driver for copper mountain S5048 network analyzer:
from typing import Union
from functools import partial
import logging
import numpy as np
from qcodes.instrument.visa import VisaInstrument
from qcodes.instrument.parameter import ArrayParameter
import qcodes.utils.validators as vals
log = logging.getLogger(name)
_unit_map = {'Log Mag': 'dB',
'Phase': 'degree',
'Group Delay': None,
'Smith': 'dim. less',
'Polar': 'dim. less',
'Lin mag': 'dim. less',
'Real': None,
'Imag': None,
'SWR': 'dim. less'}
def CMTIntParser(value: str) -> int:
"""
Small custom parser for ints
Args:
value: the VISA return string using exponential notation
"""
return int(float(value))
class TraceNotReady(Exception):
pass
class VNAS504Trace(ArrayParameter):
"""
Class to hold a the trace from the S5048
Although the trace can have two values per frequency, this
class only returns the first value
"""
def __init__(self, name, instrument):
super().__init__(name=name,
shape=(1,), # is overwritten by prepare_trace
label='', # is overwritten by prepare_trace
unit='', # is overwritten by prepare_trace
setpoint_names=('Frequency',),
setpoint_labels=('Frequency',),
setpoint_units=('Hz',),
snapshot_get=False
)
self._instrument = instrument
def prepare_trace(self):
"""
Update setpoints, units and labels
"""
# we don't trust users to keep their fingers off the front panel,
# so we query the instrument for all values
fstart = self._instrument.start_freq()
fstop = self._instrument.stop_freq()
npts = self._instrument.trace_points()
sps = np.linspace(fstart, fstop, npts)
self.setpoints = (tuple(sps),)
self.shape = (len(sps),)
self.label = self._instrument.s_parameter()
self.unit = _unit_map[self._instrument.display_format()]
self._instrument._traceready = True
def get_raw(self):
"""
Return the trace
"""
inst = self._instrument
if not inst._traceready:
raise TraceNotReady('Trace not ready. Please run prepare_trace.')
inst.write('CALC:DATA:FDAT')
inst.visa_handle.read_termination = ''
raw_resp = inst.visa_handle.read_raw()
inst.visa_handle.read_termination = '\n'
first_points = B''
for n in range((len(raw_resp)-4)//4):
first_points += raw_resp[4:][2*n*4:(2*n+1)*4]
dt = np.dtype('f')
trace1 = np.fromstring(first_points, dtype=dt)
class VNAS504(VisaInstrument):
"""
This is the QCoDeS driver for the VNAS5048 Network Analyzer
"""
def __init__(self, name: str, address: str, **kwargs) -> None:
super().__init__(name, address, terminator='\n', **kwargs)
self.add_parameter(
'start_freq',
label='Sweep start frequency',
unit='Hz',
set_cmd=partial(self.invalidate_trace, 'SENS:FREQ:STAR {}'),
get_cmd='SENS:FREQ:STAR?',
get_parser=float,
vals=vals.Numbers(20000, 4800000000))
self.add_parameter(
'stop_freq',
label='Sweep stop frequency',
unit='Hz',
set_cmd=partial(self.invalidate_trace, 'SENS:FREQ:STOP {}'),
get_cmd='SENS:FREQ:STOP?',
get_parser=float,
vals=vals.Numbers(20000, 4800000000))
self.add_parameter(
'averaging',
label='Averaging state',
set_cmd='SENS:AVER{}',
get_cmd='SENS:AVER?',
val_mapping={'ON': 1, 'OFF': 0})
self.add_parameter(
'number_of_averages',
label='Number of averages',
set_cmd='SENS:AVER:COUN{}',
get_cmd='SENS:AVER:COUN?',
get_parser=CMTIntParser,
vals=vals.Ints(0, 999))
self.add_parameter(
'trace_points',
label='Number of points in trace',
set_cmd=partial(self.invalidate_trace, 'SENS:SWE:POIN{}'),
get_cmd='SENS:SWE:POIN?',
get_parser=CMTIntParser,
vals=vals.Enum(3, 11, 26, 51, 101, 201, 401,
801, 1601))
self.add_parameter(
'sweep_time',
label='Sweep time',
set_cmd='SENS:SWE:POIN:TIME{}',
get_cmd='SENS:SWE:POIN:TIME?',
unit='s',
get_parser=float,
vals=vals.Numbers(0, 0.3),
)
self.add_parameter(
'output_power',
label='Output power',
unit='dBm',
set_cmd='SOUR:POW{}',
get_cmd='SOUR:POW?',
get_parser=float,
vals=vals.Numbers(-80, 20))
self.add_parameter(
's_parameter',
label='S-parameter',
set_cmd=self._s_parameter_setter,
get_cmd=self._s_parameter_getter)
# DISPLAY / Y SCALE PARAMETERS
self.add_parameter(
'display_format',
label='Display format',
set_cmd=self._display_format_setter,
get_cmd=self._display_format_getter)
# TODO: update this on startup and via display format
self.add_parameter(
'display_reference',
label='Display reference level',
unit=None, # will be set by display_format
get_cmd='DISP:WIND:TRAC:Y:RLEV?',
set_cmd='DISP:WIND:TRAC:Y:RLEV{}',
get_parser=float,
vals=vals.Numbers(-10e-18, 1e18))
self.add_parameter(
'display_scale',
label='Display scale',
unit=None, # will be set by display_format
get_cmd='DISP:WIND:TRAC:Y:PDIV?',
set_cmd='DISP:WIND:TRAC:Y:PDIV{}',
get_parser=float,
vals=vals.Numbers(-10e-18, 1e18))
self.add_parameter(
name='trace',
parameter_class=VNAS504Trace)
# Startup
self.startup()
self.connect_message()
def reset(self) -> None:
"""
Resets the instrument to factory default state
"""
# use OPC to make sure we wait for operation to finish
self.ask('*OPC?;SYST:PRES')
def run_continously(self) -> None:
"""
Set the instrument in run continously mode
"""
self.write('INIT:CONT:ALL:ON')
def run_N_times(self, N: int) -> None:
"""
Run N sweeps and then hold. We wait for a response before returning
"""
st = self.sweep_time.get_latest()
old_timeout = self.visa_handle.timeout
if N not in range(1, 1000):
raise ValueError('Can not run {} times.'.format(N) +
' please select a number from 1-999.')
# set a longer timeout, to not timeout during the sweep
new_timeout = 1000*st*N + 1000
self.visa_handle.timeout = new_timeout
log.debug('Making {} blocking sweeps.'.format(N) +
' Setting VISA timeout to {} s.'.format(new_timeout/1000))
self.ask('*OPC?;NUMG{}'.format(N))
self.visa_handle.timeout = old_timeout
def invalidate_trace(self, cmd: str,
value: Union[float, int, str]) -> None:
"""
Wrapper for set_cmds that make the trace not ready
"""
self._traceready = False
self.write(cmd.format(value))
def startup(self) -> None:
self._traceready = False
self.display_format(self.display_format())
def _s_parameter_setter(self, param: str) -> None:
"""
set_cmd for the s_parameter parameter
"""
if param not in ['S11', 'S12', 'S21', 'S22']:
raise ValueError('Cannot set s-parameter to {}')
# the trace labels changes
self._traceready = False
self.write(f"CALC:PAR:DEF \"{param}\"")
def _s_parameter_getter(self) -> str:
"""
get_cmd for the s_parameter parameter
"""
for cmd in ['S11', 'S12', 'S21', 'S22']:
resp = self.ask('CALC:PAR:DEF?')
if resp in ['1', '1\n']:
break
return cmd.replace('?', '')
def _display_format_setter(self, fmt: str) -> None:
"""
set_cmd for the display_format parameter
"""
val_mapping = {'Log Mag': 'MLOG',
'Phase': 'PHAS',
'Group Delay': 'GDEL',
'Smith': 'SMIT',
'Polar': 'POL',
'Lin Mag': 'MLIN',
'Real': 'REAL',
'Imag': 'IMAG',
'SWR': 'SWR'}
if fmt not in val_mapping.keys():
raise ValueError('Cannot set display_format to {}.'.format(fmt))
self._traceready = False
self.display_reference.unit = _unit_map[fmt]
self.display_scale.unit = _unit_map[fmt]
self.write(f"CALC:FORM \"{fmt}\"")
def _display_format_getter(self) -> str:
"""
get_cmd for the display_format parameter
"""
val_mapping = {'MLOG': 'Log Mag',
'PHAS': 'Phase',
'GDEL': 'Group Delay',
'SMIT': 'Smith',
'POL': 'Polar',
'MLIN': 'Lin Mag',
'REAL': 'Real',
'IMAG': 'Imag',
'SWR': 'SWR'}
# keep asking until we find the currently used format
for cmd in val_mapping.keys():
resp = self.ask('CALC:FORM?'.format(cmd))
if resp in ['1', '1\n']:
break
return val_mapping[cmd]
I have followed the QCoDeS driver script of HP 8753D Network Analyzer. My driver can connect to the instrument, but not responding to the commands. Can anyone please let me know where I am making mistakes. Thank you very much.
@Saba-Mehshar please open this as a pull request like this one #75
closing now that #78 is merged