1# Copyright 2016 Google Inc. 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14""" 15This module has the class for controlling Mini-Circuits RCDAT series 16attenuators over Telnet. 17 18See http://www.minicircuits.com/softwaredownload/Prog_Manual-6-Programmable_Attenuator.pdf 19""" 20 21from mobly.controllers import attenuator 22from mobly.controllers.attenuator_lib import telnet_scpi_client 23 24 25class AttenuatorDevice: 26 """This provides a specific telnet-controlled implementation of 27 AttenuatorDevice for Mini-Circuits RC-DAT attenuators. 28 29 Attributes: 30 path_count: The number of signal attenuation path this device has. 31 """ 32 33 def __init__(self, path_count=1): 34 self.path_count = path_count 35 # The telnet client used to communicate with the attenuator device. 36 self._telnet_client = telnet_scpi_client.TelnetScpiClient( 37 tx_cmd_separator="\r\n", rx_cmd_separator="\r\n", prompt="") 38 39 @property 40 def is_open(self): 41 """This function returns the state of the telnet connection to the 42 underlying AttenuatorDevice. 43 44 Returns: 45 True if there is a successfully open connection to the 46 AttenuatorDevice. 47 """ 48 return bool(self._telnet_client.is_open) 49 50 def open(self, host, port=23): 51 """Opens a telnet connection to the desired AttenuatorDevice and 52 queries basic information. 53 54 Args: 55 host: A valid hostname (IP address or DNS-resolvable name) to an 56 MC-DAT attenuator instrument. 57 port: An optional port number (defaults to telnet default 23) 58 """ 59 self._telnet_client.open(host, port) 60 config_str = self._telnet_client.cmd("MN?") 61 if config_str.startswith("MN="): 62 config_str = config_str[len("MN="):] 63 self.properties = dict( 64 zip(['model', 'max_freq', 'max_atten'], config_str.split("-", 2))) 65 self.max_atten = float(self.properties['max_atten']) 66 67 def close(self): 68 """Closes a telnet connection to the desired attenuator device. 69 70 This should be called as part of any teardown procedure prior to the 71 attenuator instrument leaving scope. 72 """ 73 if self.is_open: 74 self._telnet_client.close() 75 76 def set_atten(self, idx, value): 77 """Sets the attenuation value for a particular signal path. 78 79 Args: 80 idx: Zero-based index int which is the identifier for a particular 81 signal path in an instrument. For instruments that only has one 82 channel, this is ignored by the device. 83 value: A float that is the attenuation value to set. 84 85 Raises: 86 Error: The underlying telnet connection to the instrument is not 87 open. 88 IndexError: The index of the attenuator is greater than the maximum 89 index of the underlying instrument. 90 ValueError: The requested set value is greater than the maximum 91 attenuation value. 92 """ 93 if not self.is_open: 94 raise attenuator.Error("Connection to attenuator at %s is not open!" % 95 self._telnet_client.host) 96 if idx + 1 > self.path_count: 97 raise IndexError("Attenuator index out of range!", self.path_count, idx) 98 if value > self.max_atten: 99 raise ValueError("Attenuator value out of range!", self.max_atten, value) 100 # The actual device uses one-based index for channel numbers. 101 self._telnet_client.cmd("CHAN:%s:SETATT:%s" % (idx + 1, value)) 102 103 def get_atten(self, idx=0): 104 """This function returns the current attenuation from an attenuator at a 105 given index in the instrument. 106 107 Args: 108 idx: This zero-based index is the identifier for a particular 109 attenuator in an instrument. 110 111 Raises: 112 Error: The underlying telnet connection to the instrument is not 113 open. 114 115 Returns: 116 A float that is the current attenuation value. 117 """ 118 if not self.is_open: 119 raise attenuator.Error("Connection to attenuator at %s is not open!" % 120 self._telnet_client.host) 121 if idx + 1 > self.path_count or idx < 0: 122 raise IndexError("Attenuator index out of range!", self.path_count, idx) 123 telnet_cmd = ":ATT?" if self.path_count == 1 else "CHAN:%s:ATT?" % (idx + 1) 124 atten_val_str = self._telnet_client.cmd(telnet_cmd) 125 atten_val = float(atten_val_str) 126 return atten_val 127