Source code for atomiq.components.electronics.voltagesource

from __future__ import annotations

from atomiq.components.primitives import Component, Parametrizable

from artiq.experiment import kernel
from artiq.language.types import TFloat, TInt32, TBool
from artiq.language.core import delay


[docs] class VoltageSource(Component, Parametrizable): """Voltage Source This abstract class represents any device that can output a defined, controllable voltage. Args: min_voltage: The minimum voltage the device can output [V] max_voltage: The maximum voltage the device can output [V] default_ramp_steps: The default number of steps that this device should use if the voltage is ramped. This value is only used if no `ramp_steps` are given in the :func:`ramp_voltage` method. """ kernel_invariants = {"min_voltage", "max_voltage", "default_ramp_steps"} def __init__(self, min_voltage: TFloat = float('-inf'), max_voltage: TFloat = float('inf'), default_ramp_steps: TInt32 = 30, *args, **kwargs): Component.__init__(self, *args, **kwargs) Parametrizable.__init__(self, ["voltage"]) self.min_voltage = min_voltage self.max_voltage = max_voltage self.default_ramp_steps = default_ramp_steps
[docs] @kernel def set_voltage(self, voltage: TFloat): """ Set the voltage delivered by the voltage source Args: voltage: Voltage in V """ if voltage >= self.min_voltage and voltage <= self.max_voltage: self._set_voltage(voltage) else: self.experiment.log.warning(self.identifier + ": Trying to set voltage {0}V which is out of limits [{1}V, {2}V]", [voltage, self.min_voltage, self.max_voltage])
@kernel def _set_voltage(self, voltage: TFloat): raise NotImplementedError("Implement `set_voltage()` for your voltage source") @kernel(flags={"fast-math"}) def _ramp_voltage(self, duration: TFloat, voltage_start: TFloat, voltage_end: TFloat, ramp_timestep: TFloat = 200e-6): """ This method implements a stupid ramp on an abstract level. This will most likely work but be slow. If your hardware has native support for ramping, please override this function when you inherit from VoltageSource """ n = int(duration / ramp_timestep) for i in range(n + 1): self._set_voltage(voltage_start + i * (voltage_end - voltage_start) / n) delay(ramp_timestep)
[docs] @kernel(flags={"fast-math"}) def ramp_voltage(self, duration: TFloat, voltage_start: TFloat, voltage_end: TFloat, ramp_timestep: TFloat = -1.0, ramp_steps: TInt32 = -1): """Ramp voltage over a given duration. This method advances the timeline by `duration` Args: duration: ramp duration [s] voltage_start: initial voltage [V] voltage_end: end voltage [V] """ if self.min_voltage <= voltage_start <= self.max_voltage \ and self.min_voltage <= voltage_end <= self.max_voltage: if ramp_timestep > 0: pass elif ramp_steps > 0: ramp_timestep = duration/ramp_steps else: ramp_timestep = duration/self.default_ramp_steps self._ramp_voltage(duration, voltage_start, voltage_end, ramp_timestep) else: self.experiment.log.warning(self.identifier + ": Trying to ramp voltage {0}V -> {1}V which is out of limits [{2}V, {3}V]", [voltage_start, voltage_end, self.min_voltage, self.max_voltage])
[docs] class DAC(Component, Parametrizable): """A DAC with multiple channels Args: num_chan: Number of channels the DAC has """ kernel_invariants = {"num_chan"} def __init__(self, num_chan: TInt32, *args, **kwargs): Component.__init__(self, *args, **kwargs) Parametrizable.__init__(self, [f"voltage{i}" for i in range(num_chan)]) self.num_chan = num_chan
[docs] @kernel def set_channel_voltage(self, channel: TInt32, voltage: TFloat): pass
[docs] @kernel def update(self): raise NotImplementedError("Implement the update method for your DAC")
[docs] class DACChannel(VoltageSource): kernel_invariants = {"dac_device", "channel"} def __init__(self, dac_device: DAC, channel: TInt32, *args, **kwargs): """A single channel of a (possibly multichannel) DAC A single channel of a DAC is a digitally controlled voltage source. Thus, this class inherits from :class:`~atomiq.components.electronics.voltagesource.VoltageSource`. Args: dac_device: the DAC the channel belongs to channel: the number of the channel """ VoltageSource.__init__(self, *args, **kwargs) self.dac_device = dac_device self.channel = channel @kernel def _set_voltage(self, voltage: TFloat, update_dac: TBool = True): self.dac_device.set_channel_voltage(self.channel, voltage) if update_dac: self.dac_device.update()