Source code for instruments.holzworth.holzworth_hs9000

#!/usr/bin/env python
"""
Provides support for the Holzworth HS9000
"""

# IMPORTS #####################################################################


from instruments.units import ureg as u

from instruments.abstract_instruments.signal_generator import SignalGenerator, SGChannel
from instruments.util_fns import (
    ProxyList,
    split_unit_str,
    bounded_unitful_property,
    bool_property,
)

# CLASSES #####################################################################


[docs] class HS9000(SignalGenerator): """ Communicates with a `Holzworth HS-9000 series`_ multi-channel frequency synthesizer. .. _Holzworth HS-9000 series: http://www.holzworth.com/synthesizers-multi.htm """ # INNER CLASSES #
[docs] class Channel(SGChannel): """ Class representing a physical channel on the Holzworth HS9000 .. warning:: This class should NOT be manually created by the user. It is designed to be initialized by the `HS9000` class. """ def __init__(self, hs, idx_chan): self._hs = hs self._idx = idx_chan # We unpacked the channel index from the string of the form "CH1", # in order to make the API more Pythonic, but now we need to put # it back. # Some channel names, like "REF", are special and are preserved # as strs. self._ch_name = ( idx_chan if isinstance(idx_chan, str) else f"CH{idx_chan + 1}" ) # PRIVATE METHODS #
[docs] def sendcmd(self, cmd): """ Function used to send a command to the instrument while wrapping the command with the neccessary identifier for the channel. :param str cmd: Command that will be sent to the instrument after being prefixed with the channel identifier """ self._hs.sendcmd(f":{self._ch_name}:{cmd}")
[docs] def query(self, cmd): """ Function used to send a command to the instrument while wrapping the command with the neccessary identifier for the channel. :param str cmd: Command that will be sent to the instrument after being prefixed with the channel identifier :return: The result from the query :rtype: `str` """ return self._hs.query(f":{self._ch_name}:{cmd}")
# STATE METHODS #
[docs] def reset(self): """ Resets the setting of the specified channel Example usage: >>> import instruments as ik >>> hs = ik.holzworth.HS9000.open_tcpip("192.168.0.2", 8080) >>> hs.channel[0].reset() """ self.sendcmd("*RST")
[docs] def recall_state(self): """ Recalls the state of the specified channel from memory. Example usage: >>> import instruments as ik >>> hs = ik.holzworth.HS9000.open_tcpip("192.168.0.2", 8080) >>> hs.channel[0].recall_state() """ self.sendcmd("*RCL")
[docs] def save_state(self): """ Saves the current state of the specified channel. Example usage: >>> import instruments as ik >>> hs = ik.holzworth.HS9000.open_tcpip("192.168.0.2", 8080) >>> hs.channel[0].save_state() """ self.sendcmd("*SAV")
# PROPERTIES # @property def temperature(self): """ Gets the current temperature of the specified channel. :units: As specified by the instrument. :rtype: `~pint.Quantity` """ val, units = split_unit_str(self.query("TEMP?")) units = f"deg{units}" return u.Quantity(val, units) frequency, frequency_min, frequency_max = bounded_unitful_property( "FREQ", units=u.GHz, doc=""" Gets/sets the frequency of the specified channel. When setting, values are bounded between what is returned by `frequency_min` and `frequency_max`. Example usage: >>> import instruments as ik >>> hs = ik.holzworth.HS9000.open_tcpip("192.168.0.2", 8080) >>> print(hs.channel[0].frequency) >>> print(hs.channel[0].frequency_min) >>> print(hs.channel[0].frequency_max) :type: `~pint.Quantity` :units: As specified or assumed to be of units GHz """, ) power, power_min, power_max = bounded_unitful_property( "PWR", units=u.dBm, doc=""" Gets/sets the output power of the specified channel. When setting, values are bounded between what is returned by `power_min` and `power_max`. Example usage: >>> import instruments as ik >>> hs = ik.holzworth.HS9000.open_tcpip("192.168.0.2", 8080) >>> print(hs.channel[0].power) >>> print(hs.channel[0].power_min) >>> print(hs.channel[0].power_max) :type: `~pint.Quantity` :units: `instruments.units.dBm` """, ) phase, phase_min, phase_max = bounded_unitful_property( "PHASE", units=u.degree, doc=""" Gets/sets the output phase of the specified channel. When setting, values are bounded between what is returned by `phase_min` and `phase_max`. Example usage: >>> import instruments as ik >>> hs = ik.holzworth.HS9000.open_tcpip("192.168.0.2", 8080) >>> print(hs.channel[0].phase) >>> print(hs.channel[0].phase_min) >>> print(hs.channel[0].phase_max) :type: `~pint.Quantity` :units: As specified or assumed to be of units degrees """, ) output = bool_property( "PWR:RF", inst_true="ON", inst_false="OFF", set_fmt="{}:{}", doc=""" Gets/sets the output status of the channel. Setting to `True` will turn the channel's output stage on, while a value of `False` will turn it off. Example usage: >>> import instruments as ik >>> hs = ik.holzworth.HS9000.open_tcpip("192.168.0.2", 8080) >>> print(hs.channel[0].output) >>> hs.channel[0].output = True :type: `bool` """, )
# PROXY LIST ## def _channel_idxs(self): """ Internal function used to get the list of valid channel names to be used by `HS9000.channel` :return: A list of valid channel indicies :rtype: `list` of `int` and `str` """ # The command :ATTACH? returns a string of the form ":CH1:CH2" to # indicate what channels are attached to the internal USB bus. # We convert what channel names we can to integers, and leave the # rest as strings. return [ ( int(ch_name.replace("CH", "")) - 1 if ch_name.startswith("CH") else ch_name.strip() ) for ch_name in self.query(":ATTACH?").split(":") if ch_name ] @property def channel(self): """ Gets a specific channel on the HS9000. The desired channel is accessed like one would access a list. Example usage: >>> import instruments as ik >>> hs = ik.holzworth.HS9000.open_tcpip("192.168.0.2", 8080) >>> print(hs.channel[0].frequency) :return: A channel object for the HS9000 :rtype: `~HS9000.Channel` """ return ProxyList(self, self.Channel, self._channel_idxs()) # OTHER PROPERTIES # @property def name(self): """ Gets identification string of the HS9000 :return: The string as usually returned by ``*IDN?`` on SCPI instruments :rtype: `str` """ # This is a weird one; the HS-9000 associates the :IDN? command # with each individual channel, though we want it to be a synthesizer- # wide property. To solve this, we assume that CH1 is always a channel # and ask its name. return self.channel[0].query("IDN?") @property def ready(self): """ Gets the ready status of the HS9000. :return: If the instrument is ready for operation :rtype: `bool` """ return "Ready" in self.query(":COMM:READY?")