Source code for qcodes.instrument_drivers.Keysight.KeysightAgilent_33XXX

import logging
from functools import partial
from typing import TYPE_CHECKING

from typing_extensions import deprecated

from qcodes import validators as vals
from qcodes.instrument import (
    Instrument,
    InstrumentBaseKWArgs,
    InstrumentChannel,
    VisaInstrument,
    VisaInstrumentKWArgs,
)
from qcodes.utils import QCoDeSDeprecationWarning

from .private.error_handling import KeysightErrorQueueMixin

if TYPE_CHECKING:
    from typing_extensions import Unpack

    from qcodes.parameters import Parameter

log = logging.getLogger(__name__)


# This is to be the grand unified driver superclass for
# The Keysight/Agilent/HP Waveform generators of series
# 33200, 33500, and 33600


[docs] class Keysight33xxxOutputChannel(InstrumentChannel): """ Class to hold the output channel of a Keysight 33xxxx waveform generator. """ def __init__( self, parent: Instrument, name: str, channum: int, **kwargs: "Unpack[InstrumentBaseKWArgs]", ) -> None: """ Args: parent: The instrument to which the channel is attached. name: The name of the channel channum: The number of the channel in question (1-2) **kwargs: kwargs are forwarded to base class. """ super().__init__(parent, name, **kwargs) def val_parser(parser: type, inputstring: str) -> float | int: """ Parses return values from instrument. Meant to be used when a query can return a meaningful finite number or a numeric representation of infinity Args: parser: Either int or float, what to return in finite cases inputstring: The raw return value """ inputstring = inputstring.strip() if float(inputstring) == 9.9e37: output = float('inf') else: output = float(inputstring) if parser is int: output = parser(output) return output self.model = self._parent.model self.function_type: Parameter = self.add_parameter( "function_type", label=f"Channel {channum} function type", set_cmd=f"SOURce{channum}:FUNCtion {{}}", get_cmd=f"SOURce{channum}:FUNCtion?", get_parser=str.rstrip, vals=vals.Enum( "SIN", "SQU", "TRI", "RAMP", "PULS", "PRBS", "NOIS", "ARB", "DC" ), ) """Parameter function_type""" self.frequency_mode: Parameter = self.add_parameter( "frequency_mode", label=f"Channel {channum} frequency mode", set_cmd=f"SOURce{channum}:FREQuency:MODE {{}}", get_cmd=f"SOURce{channum}:FREQuency:MODE?", get_parser=str.rstrip, vals=vals.Enum("CW", "LIST", "SWEEP", "FIXED"), ) """Parameter frequency_mode""" max_freq = self._parent._max_freqs[self.model] self.frequency: Parameter = self.add_parameter( "frequency", label=f"Channel {channum} frequency", set_cmd=f"SOURce{channum}:FREQuency {{}}", get_cmd=f"SOURce{channum}:FREQuency?", get_parser=float, unit="Hz", # TODO: max. freq. actually really tricky vals=vals.Numbers(1e-6, max_freq), ) """Parameter frequency""" self.phase: Parameter = self.add_parameter( "phase", label=f"Channel {channum} phase", set_cmd=f"SOURce{channum}:PHASe {{}}", get_cmd=f"SOURce{channum}:PHASe?", get_parser=float, unit="deg", vals=vals.Numbers(0, 360), ) """Parameter phase""" self.amplitude_unit: Parameter = self.add_parameter( "amplitude_unit", label=f"Channel {channum} amplitude unit", set_cmd=f"SOURce{channum}:VOLTage:UNIT {{}}", get_cmd=f"SOURce{channum}:VOLTage:UNIT?", vals=vals.Enum("VPP", "VRMS", "DBM"), get_parser=str.rstrip, ) """Parameter amplitude_unit""" self.amplitude: Parameter = self.add_parameter( "amplitude", label=f"Channel {channum} amplitude", set_cmd=f"SOURce{channum}:VOLTage {{}}", get_cmd=f"SOURce{channum}:VOLTage?", unit="", # see amplitude_unit get_parser=float, ) """Parameter amplitude""" self.offset: Parameter = self.add_parameter( "offset", label=f"Channel {channum} voltage offset", set_cmd=f"SOURce{channum}:VOLTage:OFFSet {{}}", get_cmd=f"SOURce{channum}:VOLTage:OFFSet?", unit="V", get_parser=float, ) """Parameter offset""" self.output: Parameter = self.add_parameter( "output", label=f"Channel {channum} output state", set_cmd=f"OUTPut{channum} {{}}", get_cmd=f"OUTPut{channum}?", val_mapping={"ON": 1, "OFF": 0}, ) """Parameter output""" self.ramp_symmetry: Parameter = self.add_parameter( "ramp_symmetry", label=f"Channel {channum} ramp symmetry", set_cmd=f"SOURce{channum}:FUNCtion:RAMP:SYMMetry {{}}", get_cmd=f"SOURce{channum}:FUNCtion:RAMP:SYMMetry?", get_parser=float, unit="%", vals=vals.Numbers(0, 100), ) """Parameter ramp_symmetry""" self.pulse_width: Parameter = self.add_parameter( "pulse_width", label=f"Channel {channum} pulse width", set_cmd=f"SOURce{channum}:FUNCtion:PULSE:WIDTh {{}}", get_cmd=f"SOURce{channum}:FUNCtion:PULSE:WIDTh?", get_parser=float, unit="S", ) """Parameter pulse_width""" # TRIGGER MENU self.trigger_source: Parameter = self.add_parameter( "trigger_source", label=f"Channel {channum} trigger source", set_cmd=f"TRIGger{channum}:SOURce {{}}", get_cmd=f"TRIGger{channum}:SOURce?", vals=vals.Enum("IMM", "EXT", "TIM", "BUS"), get_parser=str.rstrip, ) """Parameter trigger_source""" self.trigger_slope: Parameter = self.add_parameter( "trigger_slope", label=f"Channel {channum} trigger slope", set_cmd=f"TRIGger{channum}:SLOPe {{}}", get_cmd=f"TRIGger{channum}:SLOPe?", vals=vals.Enum("POS", "NEG"), get_parser=str.rstrip, ) """Parameter trigger_slope""" # Older models do not have all the fancy trigger options if self._parent.model[2] in ["5", "6"]: self.trigger_count: Parameter = self.add_parameter( "trigger_count", label=f"Channel {channum} trigger count", set_cmd=f"TRIGger{channum}:COUNt {{}}", get_cmd=f"TRIGger{channum}:COUNt?", vals=vals.Ints(1, 1000000), get_parser=partial(val_parser, int), ) """Parameter trigger_count""" self.trigger_delay: Parameter = self.add_parameter( "trigger_delay", label=f"Channel {channum} trigger delay", set_cmd=f"TRIGger{channum}:DELay {{}}", get_cmd=f"TRIGger{channum}:DELay?", vals=vals.Numbers(0, 1000), get_parser=float, unit="s", ) """Parameter trigger_delay""" self.trigger_timer: Parameter = self.add_parameter( "trigger_timer", label=f"Channel {channum} trigger timer", set_cmd=f"TRIGger{channum}:TIMer {{}}", get_cmd=f"TRIGger{channum}:TIMer?", vals=vals.Numbers(1e-6, 8000), get_parser=float, ) """Parameter trigger_timer""" # TODO: trigger level doesn't work remotely. Why? # output menu self.output_polarity: Parameter = self.add_parameter( "output_polarity", label=f"Channel {channum} output polarity", set_cmd=f"OUTPut{channum}:POLarity {{}}", get_cmd=f"OUTPut{channum}:POLarity?", get_parser=str.rstrip, vals=vals.Enum("NORM", "INV"), ) """Parameter output_polarity""" # BURST MENU self.burst_state: Parameter = self.add_parameter( "burst_state", label=f"Channel {channum} burst state", set_cmd=f"SOURce{channum}:BURSt:STATe {{}}", get_cmd=f"SOURce{channum}:BURSt:STATe?", val_mapping={"ON": 1, "OFF": 0}, vals=vals.Enum("ON", "OFF"), ) """Parameter burst_state""" self.burst_mode: Parameter = self.add_parameter( "burst_mode", label=f"Channel {channum} burst mode", set_cmd=f"SOURce{channum}:BURSt:MODE {{}}", get_cmd=f"SOURce{channum}:BURSt:MODE?", get_parser=str.rstrip, val_mapping={"N Cycle": "TRIG", "Gated": "GAT"}, vals=vals.Enum("N Cycle", "Gated"), ) """Parameter burst_mode""" self.burst_ncycles: Parameter = self.add_parameter( "burst_ncycles", label=f"Channel {channum} burst no. of cycles", set_cmd=f"SOURce{channum}:BURSt:NCYCles {{}}", get_cmd=f"SOURce{channum}:BURSt:NCYCLes?", get_parser=partial(val_parser, int), vals=vals.MultiType(vals.Ints(1), vals.Enum("MIN", "MAX", "INF")), ) """Parameter burst_ncycles""" self.burst_phase: Parameter = self.add_parameter( "burst_phase", label=f"Channel {channum} burst start phase", set_cmd=f"SOURce{channum}:BURSt:PHASe {{}}", get_cmd=f"SOURce{channum}:BURSt:PHASe?", vals=vals.Numbers(-360, 360), unit="degrees", get_parser=float, ) """Parameter burst_phase""" self.burst_polarity: Parameter = self.add_parameter( "burst_polarity", label=f"Channel {channum} burst gated polarity", set_cmd=f"SOURce{channum}:BURSt:GATE:POLarity {{}}", get_cmd=f"SOURce{channum}:BURSt:GATE:POLarity?", vals=vals.Enum("NORM", "INV"), ) """Parameter burst_polarity""" self.burst_int_period: Parameter = self.add_parameter( "burst_int_period", label=(f"Channel {channum} burst internal period"), set_cmd=f"SOURce{channum}:BURSt:INTernal:PERiod {{}}", get_cmd=f"SOURce{channum}:BURSt:INTernal:PERiod?", unit="s", vals=vals.Numbers(1e-6, 8e3), get_parser=float, docstring=( "The burst period is the time " "between the starts of consecutive " "bursts when trigger is immediate." ), ) """The burst period is the time between the starts of consecutive bursts when trigger is immediate."""
OutputChannel = Keysight33xxxOutputChannel
[docs] class Keysight33xxxSyncChannel(InstrumentChannel): """ Class to hold the sync output of a Keysight 33xxxx waveform generator. Has very few parameters for single channel instruments. """ def __init__( self, parent: Instrument, name: str, **kwargs: "Unpack[InstrumentBaseKWArgs]", ): super().__init__(parent, name, **kwargs) self.output: Parameter = self.add_parameter( "output", label="Sync output state", set_cmd="OUTPut:SYNC {}", get_cmd="OUTPut:SYNC?", val_mapping={"ON": 1, "OFF": 0}, vals=vals.Enum("ON", "OFF"), ) """Parameter output""" if parent.num_channels == 2: self.source: Parameter = self.add_parameter( "source", label="Source of sync function", set_cmd="OUTPut:SYNC:SOURce {}", get_cmd="OUTPut:SYNC:SOURce?", val_mapping={1: "CH1", 2: "CH2"}, vals=vals.Enum(1, 2), ) """Parameter source"""
SyncChannel = Keysight33xxxSyncChannel
[docs] class Keysight33xxx(KeysightErrorQueueMixin, VisaInstrument): """ Base class for Keysight/Agilent 33XXX waveform generators. Not to be instantiated directly. """ default_terminator = "\n" def __init__( self, name: str, address: str, silent: bool = False, **kwargs: "Unpack[VisaInstrumentKWArgs]", ): """ Args: name: The name of the instrument used internally by QCoDeS. Must be unique. address: The VISA resource name. silent: If True, no connect message is printed. **kwargs: kwargs are forwarded to base class. """ super().__init__(name, address, **kwargs) self.model = self.IDN()['model'] ####################################################################### # Here go all model specific traits # TODO: Fill out this dict with all models no_of_channels = {'33210A': 1, '33250A': 1, '33511B': 1, '33512B': 2, '33522B': 2, '33622A': 2, '33510B': 2, } self._max_freqs = {'33210A': 10e6, '33511B': 20e6, '33512B': 20e6, '33250A': 80e6, '33522B': 30e6, '33622A': 120e6, '33510B': 20e6, } self.num_channels = no_of_channels[self.model] for i in range(1, self.num_channels + 1): channel = Keysight33xxxOutputChannel(self, f"ch{i}", i) self.add_submodule(f"ch{i}", channel) sync = Keysight33xxxSyncChannel(self, "sync") self.add_submodule("sync", sync) self.add_function('force_trigger', call_cmd='*TRG') self.add_function('sync_channel_phases', call_cmd='PHAS:SYNC') if not silent: self.connect_message()
@deprecated( "The base class for Keysight33xxx waveform generators has been renamed to Keysight33xxx", category=QCoDeSDeprecationWarning, ) class WaveformGenerator_33XXX(Keysight33xxx): pass