1#!/usr/bin/env python3 2 3# Copyright 2016- The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16""" 17Class for HTTP control of Mini-Circuits RCDAT series attenuators 18 19This class provides a wrapper to the MC-RCDAT attenuator modules for purposes 20of simplifying and abstracting control down to the basic necessities. It is 21not the intention of the module to expose all functionality, but to allow 22interchangeable HW to be used. 23 24See http://www.minicircuits.com/softwaredownload/Prog_Manual-6-Programmable_Attenuator.pdf 25""" 26 27import urllib 28from acts.controllers import attenuator 29 30 31class AttenuatorInstrument(attenuator.AttenuatorInstrument): 32 """A specific HTTP-controlled implementation of AttenuatorInstrument for 33 Mini-Circuits RC-DAT attenuators. 34 35 With the exception of HTTP-specific commands, all functionality is defined 36 by the AttenuatorInstrument class. 37 """ 38 39 def __init__(self, num_atten=1): 40 super(AttenuatorInstrument, self).__init__(num_atten) 41 self._ip_address = None 42 self._port = None 43 self._timeout = None 44 self.address = None 45 46 def open(self, host, port=80, timeout=2): 47 """Initializes the AttenuatorInstrument and queries basic information. 48 49 Args: 50 host: A valid hostname (IP address or DNS-resolvable name) to an 51 MC-DAT attenuator instrument. 52 port: An optional port number (defaults to http default 80) 53 timeout: An optional timeout for http requests 54 """ 55 self._ip_address = host 56 self._port = port 57 self._timeout = timeout 58 self.address = host 59 60 att_req = urllib.request.urlopen('http://{}:{}/MN?'.format( 61 self._ip_address, self._port)) 62 config_str = att_req.read().decode('utf-8') 63 if not config_str.startswith('MN='): 64 raise attenuator.InvalidDataError( 65 'Attenuator returned invalid data. Attenuator returned: {}'. 66 format(config_str)) 67 68 config_str = config_str[len('MN='):] 69 self.properties = dict( 70 zip(['model', 'max_freq', 'max_atten'], config_str.split('-', 2))) 71 self.max_atten = float(self.properties['max_atten']) 72 73 def is_open(self): 74 """Returns True if the AttenuatorInstrument has an open connection. 75 76 Since this controller is based on HTTP requests, there is no connection 77 required and the attenuator is always ready to accept requests. 78 """ 79 return True 80 81 def close(self): 82 """Closes the connection to the attenuator. 83 84 Since this controller is based on HTTP requests, there is no connection 85 teardowns required. 86 """ 87 pass 88 89 def set_atten(self, idx, value, strict_flag=True): 90 """This function sets the attenuation of an attenuator given its index 91 in the instrument. 92 93 Args: 94 idx: A zero-based index that identifies a particular attenuator in 95 an instrument. For instruments that only have one channel, this 96 is ignored by the device. 97 value: A floating point value for nominal attenuation to be set. 98 strict_flag: if True, function raises an error when given out of 99 bounds attenuation values, if false, the function sets out of 100 bounds values to 0 or max_atten. 101 102 Raises: 103 InvalidDataError if the attenuator does not respond with the 104 expected output. 105 """ 106 if not (0 <= idx < self.num_atten): 107 raise IndexError('Attenuator index out of range!', self.num_atten, 108 idx) 109 110 if value > self.max_atten and strict_flag: 111 raise ValueError('Attenuator value out of range!', self.max_atten, 112 value) 113 # The actual device uses one-based index for channel numbers. 114 att_req = urllib.request.urlopen( 115 'http://{}:{}/CHAN:{}:SETATT:{}'.format( 116 self._ip_address, self._port, idx + 1, value), 117 timeout=self._timeout) 118 att_resp = att_req.read().decode('utf-8') 119 if att_resp != '1': 120 raise attenuator.InvalidDataError( 121 'Attenuator returned invalid data. Attenuator returned: {}'. 122 format(att_resp)) 123 124 def get_atten(self, idx): 125 """Returns the current attenuation of the attenuator at the given index. 126 127 Args: 128 idx: The index of the attenuator. 129 130 Raises: 131 InvalidDataError if the attenuator does not respond with the 132 expected outpu 133 134 Returns: 135 the current attenuation value as a float 136 """ 137 if not (0 <= idx < self.num_atten): 138 raise IndexError('Attenuator index out of range!', self.num_atten, 139 idx) 140 att_req = urllib.request.urlopen( 141 'http://{}:{}/CHAN:{}:ATT?'.format(self._ip_address, self.port, 142 idx + 1), 143 timeout=self._timeout) 144 att_resp = att_req.read().decode('utf-8') 145 try: 146 atten_val = float(att_resp) 147 except: 148 raise attenuator.InvalidDataError( 149 'Attenuator returned invalid data. Attenuator returned: {}'. 150 format(att_resp)) 151 return atten_val 152