from typing import Dict, Optional
from qcodes.instrument.base import Instrument
from qcodes.utils.validators import Enum, Numbers
try:
from spirack import D5a_module
except ImportError:
raise ImportError(('The D5a_module class could not be found. '
'Try installing it using pip install spirack'))
from functools import partial
[docs]
class D5a(Instrument):
"""
Qcodes driver for the D5a DAC SPI-rack module.
functions:
- set_dacs_zero set all DACs to zero voltage
parameters:
- dacN: get and set DAC voltage
- stepsizeN get the minimum step size corresponding to the span
- spanN get and set the DAC span: '4v uni', '4v bi', or '2.5v bi'
where N is the DAC number from 1 up to 16
"""
[docs]
def __init__(self, name, spi_rack, module, inter_delay=0.1, dac_step=10e-3,
reset_voltages=False, mV=False, number_dacs=16, **kwargs):
""" Create instrument for the D5a module.
The D5a module works with volts as units. For backward compatibility
there is the option to allow mV for the dacX parameters.
The output span of the DAC module can be changed with the spanX
command. Be carefull when executing this command with a sample
connected as voltage jumps can occur.
Args:
name (str): name of the instrument.
spi_rack (SPI_rack): instance of the SPI_rack class as defined in
the spirack package. This class manages communication with the
individual modules.
module (int): module number as set on the hardware.
inter_delay (float): time in seconds, passed to dac parameters of the object
dac_step (float): max step size (V or mV), passed to dac parameters of the object
reset_voltages (bool): passed to D5a_module constructor
mV (bool): if True, then use mV as units in the dac parameters
number_dacs (int): number of DACs available. This is 8 for the D5mux
"""
super().__init__(name, **kwargs)
try:
self.d5a = D5a_module(spi_rack, module, reset_voltages=reset_voltages)
except ValueError as ex:
# A Value error on D5a with a message starting with "Span " is
# almost always caused by swapped COM port numbers of 2 SPI racks.
# An attempt is made to access a D5a module where there is none.
if str(ex).startswith("Span "):
raise Exception("Error reading D5a module. Check COM port numbers")
raise
self._number_dacs = number_dacs
self._span_set_map = {
'4v uni': 0,
'4v bi': 2,
'2v bi': 4,
}
self._span_get_map = {v: k for k, v in self._span_set_map.items()}
self.add_function('set_dacs_zero', call_cmd=self._set_dacs_zero,
docstring='Reset all dacs to zero voltage. No ramping is performed.')
if mV:
self._gain = 1e3
unit = 'mV'
else:
self._gain = 1
unit = 'V'
for i in range(self._number_dacs):
validator = self._get_validator(i)
self.add_parameter('dac{}'.format(i + 1),
label='DAC {}'.format(i + 1),
get_cmd=partial(self._get_dac, i),
set_cmd=partial(self._set_dac, i),
unit=unit,
vals=validator,
step=dac_step,
inter_delay=inter_delay)
self.add_parameter('stepsize{}'.format(i + 1),
get_cmd=partial(self.d5a.get_stepsize, i),
unit='V',
docstring='Returns the smallest voltage step of the DAC.')
self.add_parameter('span{}'.format(i + 1),
get_cmd=partial(self._get_span, i),
set_cmd=partial(self._set_span, i),
vals=Enum(*self._span_set_map.keys()),
docstring='Change the output span of the DAC. This command also updates the validator.')
[docs]
def get_idn(self) -> Dict[str, Optional[str]]:
return dict(vendor='QuTech',
model='D5a',
serial='',
firmware='')
[docs]
def set_dac_unit(self, unit: str) -> None:
"""Set the unit of dac parameters"""
allowed_values = Enum('mV', 'V')
allowed_values.validate(unit)
self._gain = {'V': 1, 'mV': 1e3}[unit]
for i in range(1, self._number_dacs + 1):
setattr(self.parameters[f'dac{i}'], 'unit', unit)
setattr(self.parameters[f'dac{i}'], 'vals', self._get_validator(i - 1))
def _set_dacs_zero(self) -> None:
for i in range(self._number_dacs):
self._set_dac(i, 0.0)
def _set_dac(self, dac: int, value: float) -> None:
self.d5a.set_voltage(dac, value / self._gain)
def _get_dac(self, dac: int) -> float:
return self._gain * self.d5a.voltages[dac]
def _get_span(self, dac: int) -> str:
return self._span_get_map[self.d5a.span[dac]]
def _set_span(self, dac: int, span_str: str) -> None:
self.d5a.change_span_update(dac, self._span_set_map[span_str])
self.parameters['dac{}'.format(
dac + 1)].vals = self._get_validator(dac)
def _get_validator(self, dac: int) -> Numbers:
span = self.d5a.span[dac]
if span == D5a_module.range_2V_bi:
validator = Numbers(-2 * self._gain, 2 * self._gain)
elif span == D5a_module.range_4V_bi:
validator = Numbers(-4 * self._gain, 4 * self._gain)
elif span == D5a_module.range_4V_uni:
validator = Numbers(0, 4 * self._gain)
else:
msg = 'The found DAC span of {} does not correspond to a known one'
raise Exception(msg.format(span))
return validator