Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improvements of the ITest driver including: #242

Merged
merged 2 commits into from
Aug 29, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
154 changes: 120 additions & 34 deletions qcodes_contrib_drivers/drivers/Bilt/ITest.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# This Python file uses the following encoding: utf-8
# Loick Le Guevel, 2019
# Etienne Dumur <[email protected]>, 2021
# Simon Zihlmann <[email protected]>, 2021
# Victor Millory <[email protected]>, 2021

from typing import Union, Tuple, Any
from functools import partial
Expand Down Expand Up @@ -42,7 +44,10 @@ def __init__(self, parent: Instrument,
get_cmd=partial(self._parent._get_voltage, chan_num),
get_parser=float,
set_cmd=partial(self._parent._set_voltage, chan_num),
vals=vals.Numbers(-50, 50)
inter_delay=self._parent._v_inter_delay,
post_delay=self._parent._v_post_delay,
step=self._parent._v_step,
vals=vals.Numbers(-12, 12)
)

self.add_parameter('i',
Expand Down Expand Up @@ -70,6 +75,7 @@ def __init__(self, parent: Instrument,
get_parser=str,
set_cmd=partial(self._parent._set_output_function, chan_num),
set_parser=str,
initial_value='exp',
vals=vals.Enum('ramp', 'exp')
)

Expand All @@ -79,55 +85,75 @@ def __init__(self, parent: Instrument,
set_cmd=partial(self._parent._set_chan_range, chan_num),
set_parser=float,
get_cmd=partial(self._parent._get_chan_range, chan_num),
get_parser=float)
get_parser=float,
vals=vals.Numbers(1.2, 12)
)

self.add_parameter('state',
docstring='State of the channel {on, off}.',
get_cmd=partial(self._parent._get_chan_state, chan_num),
set_cmd=partial(self._parent._set_chan_state, chan_num),
val_mapping=create_on_off_val_mapping(on_val='1',
off_val='0'))
off_val='0')
)

self.add_parameter('pos_sat',
get_cmd=partial(self._parent._get_chan_pos_sat, chan_num),
get_parser=str,
set_cmd=partial(self._parent._set_chan_pos_sat, chan_num),
set_parser=str,)
set_parser=str,
initial_value=12
)

self.add_parameter('neg_sat',
get_cmd=partial(self._parent._get_chan_neg_sat, chan_num),
get_parser=str,
set_cmd=partial(self._parent._set_chan_neg_sat, chan_num),
set_parser=str,)
set_parser=str,
initial_value=-12
)

self.add_parameter('bilt_name',
set_cmd=partial(self._parent._set_chan_name, chan_num),
set_parser=str,
initial_value=f'Chan{chan_num:02d}')
initial_value=f'Chan{chan_num:02d}'
)

self.add_parameter('synchronous_enable',
docstring='Is the channel in synchronous mode.',
get_cmd=None,
get_parser=bool,
set_cmd=None,
vals=vals.Bool(),
initial_value=True)
initial_value=False
)

self.add_parameter('synchronous_delay',
docstring='Time between to voltage measurement in second.',
get_cmd=None,
get_parser=float,
set_cmd=None,
vals=vals.Numbers(1e-3, 10),
initial_value=1e-3)
initial_value=1e-3
)

self.add_parameter('synchronous_threshold',
docstring='Threshold to unblock communication in volt.',
get_cmd=None,
get_parser=float,
set_cmd=None,
vals=vals.Numbers(0, 1e-3),
initial_value=1e-5)
initial_value=1e-5
)

self.add_parameter('v_autorange',
docstring='If the voltage autorange is activated.',
get_cmd=partial(self._parent._get_chan_v_autorange, chan_num),
get_parser=bool,
set_cmd=partial(self._parent._set_chan_v_autorange, chan_num),
vals=vals.Bool(),
initial_value=False
)


def start(self) -> None:
Expand All @@ -143,7 +169,11 @@ def stop(self) -> None:
"""
self._parent._set_chan_state(self.chan_num, '0')


def clear_alarm(self) -> None:
"""
Clear the alarm and warnings of the channel.
"""
self._parent._clear_chan_alarm(self.chan_num)

class iTestMultiChannelParameter(MultiChannelInstrumentParameter):
"""
Expand All @@ -162,9 +192,12 @@ def __init__(self,name:str,
address:str,
num_chans:int=16,
init_start:bool=False,
synchronous_enable:bool=True,
synchronous_enable:bool=False,
synchronous_delay:float=1,
synchronous_threshold:float=1e-5,
v_inter_delay:float=5e-3,
v_post_delay:float=45e-3, # settling time to 99%
v_step:float=20e-3,
**kwargs: Any) -> None:
"""
Instantiate the instrument.
Expand All @@ -173,15 +206,18 @@ def __init__(self,name:str,
name: The instrument name used by qcodes
address: The VISA name of the resource
num_chans: Number of channels to assign. Default: 16
init_start: If true set all channels to 0V, 1.2V range and switch
then on.
init_start: If true: set all channels to 0V, 12V range, exponential mode and switch
them on.
synchronous_enable: If true, block the communication until the set voltage
is reached. The communication is block through a simple while loop
with a waiting time "synchronous_delay" at each iteration until the
set voltage and the measured voltage difference is below
"synchronous_threshold".
synchronous_delay: Time between to voltage measurement in second.
synchronous_threshold: Threshold to unblock communication in volt.
v_inter_delay: delay in units of s between setting new value of the voltage parameter, defaults to 5e-3.
v_post_delay: delay in units of s after setting voltage parameter to final value, defaults to 45e-3.
v_step: max step size of the voltage parameter in units of V, defaults to 20e-3.

Returns:
ITest object
Expand All @@ -193,6 +229,9 @@ def __init__(self,name:str,

self.idn = self.get_idn()
self.num_chans = num_chans
self._v_inter_delay = v_inter_delay
self._v_post_delay = v_post_delay
self._v_step = v_step
self.chan_range = range(1,self.num_chans+1)

# Create the channels
Expand All @@ -205,9 +244,6 @@ def __init__(self,name:str,

channel = iTestChannel(self, name='chan{:02}'.format(i),
chan_num=i)
channel.synchronous_enable(synchronous_enable)
channel.synchronous_delay(synchronous_delay)
channel.synchronous_threshold(synchronous_threshold)
channels.append(channel)
self.add_submodule('ch{:02}'.format(i),channel)

Expand All @@ -216,13 +252,16 @@ def __init__(self,name:str,

if init_start:
for channel in self.channels:
channel.stop()
channel.v.set(0)
channel.v_range(1.2)
channel.v_range(12)
channel.v_autorange(False)
channel.synchronous_enable(False)
channel.output_mode('exp')
channel.start()

self.connect_message()


def _set_voltage(self, chan:int,
v_set:float) -> None:
"""
Expand All @@ -241,7 +280,6 @@ def _set_voltage(self, chan:int,
sleep(self.channels[chan-1].synchronous_delay())
v = self._get_voltage(chan)


def _get_voltage(self, chan:int) -> float:
"""
Get cmd for the chXX_v parameter
Expand All @@ -256,7 +294,6 @@ def _get_voltage(self, chan:int) -> float:

return float(self.ask('{}MEAS:VOLT?'.format(chan_id)))


def _get_current(self, chan:int) -> float:
"""
Get cmd for the chXX_i parameter
Expand All @@ -271,7 +308,6 @@ def _get_current(self, chan:int) -> float:

return float(self.ask('{}MEAS:CURR?'.format(chan_id)))


def _set_ramp_slope(self, chan:int,
slope:float) -> None:
"""
Expand All @@ -284,7 +320,6 @@ def _set_ramp_slope(self, chan:int,
chan_id = self.chan_to_id(chan)
self.write('{}VOLT:SLOP {:.8f}'.format(chan_id, slope))


def _get_ramp_slope(self, chan:int) -> str:
"""
Get slope of chXX
Expand All @@ -298,7 +333,6 @@ def _get_ramp_slope(self, chan:int) -> str:
chan_id = self.chan_to_id(chan)
return self.ask('{}VOLT:SLOP?'.format(chan_id))


def _set_output_function(self, chan:int,
outf:str) -> None:
"""
Expand All @@ -319,7 +353,6 @@ def _set_output_function(self, chan:int,

self.write(chan_id + 'trig:input ' + mode)


def _get_output_function(self, chan:int) -> str:
"""
Get output volage update function
Expand All @@ -339,7 +372,6 @@ def _get_output_function(self, chan:int) -> str:
else:
raise ValueError('Got unexpected output function mode: {}.'.format(mode))


def _set_chan_range(self, chan:int,
volt: float) -> None:
"""
Expand All @@ -350,8 +382,16 @@ def _set_chan_range(self, chan:int,
volt : Voltage range (1.2 or 12)
"""
chan_id = self.chan_to_id(chan)
self.write(chan_id + 'VOLT:RANGE ' + str(volt))

if self._get_chan_state(chan)=='1':
print('Channel {} is on and therefore the range cannot be changed. Turn it off first.'.format(chan))
else:
# update the pos and neg saturation parameter
self._set_chan_pos_sat(chan, abs(volt))
self._set_chan_neg_sat(chan, -abs(volt))
# self.channels[chan-1].v.vals=vals.Numbers(-abs(volt), abs(volt)) #does not work, throws an error at init since channels are not yet attached to instrument
# --> solve problem by moving all the communication functions to the level of the channel and not on the leel of instrument. Like this other parameters from the same channel are easily accessible via self.parameter
# change the range
self.write(chan_id + 'VOLT:RANGE ' + str(volt))

def _get_chan_range(self, chan:int) -> str:
"""
Expand All @@ -367,6 +407,35 @@ def _get_chan_range(self, chan:int) -> str:

return self.ask(chan_id + 'VOLT:RANGE?')[:-2]

def _get_chan_v_autorange(self, chan:int) -> bool:
"""
Get the channel voltage autorange state

Args:
chan: The 1-indexed channel number

Returns:
chXX_v_autorange parameter
"""
chan_id = self.chan_to_id(chan)
v_autorange_state = self.ask('{}VOLT:RANGE:AUTO?'.format(chan_id))

if v_autorange_state in ['1', '0'] :
return True if v_autorange_state=='1' else False
else:
raise ValueError('Unknown state output: {}'.format(v_autorange_state))
return False

def _set_chan_v_autorange(self, chan:int, state:bool) -> None:
"""
Set channel voltage autorange state

Args:
chan: The 1-indexed channel number
state: power state
"""
chan_id = self.chan_to_id(chan)
self.write(chan_id + 'VOLT:RANGE:AUTO {}'.format('1' if(state) else '0') )

def _set_chan_pos_sat(self, chan:int,
pos_sat: Union[float, str]) -> None:
Expand All @@ -376,7 +445,6 @@ def _set_chan_pos_sat(self, chan:int,
elif isinstance(pos_sat,str):
self.write(chan_id + 'VOLT:SAT:POS MAX')


def _set_chan_neg_sat(self, chan:int,
neg_sat: Union[float, str]) -> None:
chan_id = self.chan_to_id(chan)
Expand All @@ -385,17 +453,14 @@ def _set_chan_neg_sat(self, chan:int,
elif isinstance(neg_sat,str):
self.write(chan_id + 'VOLT:SAT:NEG MIN')


def _get_chan_pos_sat(self, chan:int) -> str:
chan_id = self.chan_to_id(chan)
return self.ask(chan_id + 'VOLT:SAT:POS ?')


def _get_chan_neg_sat(self, chan:int) -> str:
chan_id = self.chan_to_id(chan)
return self.ask(chan_id + 'VOLT:SAT:NEG ?')


def _get_chan_state(self, chan:int) -> str:
"""
Get channel power state
Expand All @@ -414,7 +479,6 @@ def _get_chan_state(self, chan:int) -> str:
else:
raise ValueError('Unknown state output: {}'.format(state))


def _set_chan_state(self, chan:int,
state:str) -> None:
"""
Expand All @@ -427,7 +491,6 @@ def _set_chan_state(self, chan:int,
chan_id = self.chan_to_id(chan)
self.write(chan_id + 'OUTP ' + state)


def _set_chan_name(self, chan:int,
name: str) -> None:
"""
Expand All @@ -440,6 +503,16 @@ def _set_chan_name(self, chan:int,
chan_id = self.chan_to_id(chan)
self.write(chan_id + 'chan:name "{}"'.format(name))

def _clear_chan_alarm(self, chan:int) -> None:
"""
Clear the alarm/warning for a given channel

Args:
chan: The 1-indexed channel number
"""
chan_id = self.chan_to_id(chan)
self.write(chan_id + 'LIM:CLEAR')
self.write(chan_id + 'STAT:CLEAR')

def chan_to_ic(self, chan:int) -> Tuple[int, int]:
"""
Expand All @@ -455,8 +528,21 @@ def chan_to_ic(self, chan:int) -> Tuple[int, int]:
c = chan-(i-1)*4
return i,c


def chan_to_id(self, chan:int) -> str:
i,c = self.chan_to_ic(chan)

return 'i{};c{};'.format(i,c)

def set_dacs_zero(self) -> None:
"""
Ramp all voltages to zero.
"""
for ch in self.channels:
ch.v(0)

def print_dac_voltages(self) -> None:
"""
Prints the voltage of all channels to cmdl.
"""
for ch in self.channels:
print('voltage on {}:{} {}'.format(ch.name, ch.v(), ch.v.unit))