• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3.4
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
17import importlib
18
19from acts.keys import Config
20
21ACTS_CONTROLLER_CONFIG_NAME = "Attenuator"
22ACTS_CONTROLLER_REFERENCE_NAME = "attenuators"
23
24def create(configs, logger):
25    objs = []
26    for c in configs:
27        attn_model = c["Model"]
28        # Default to telnet.
29        protocol = "telnet"
30        if "Protocol" in c:
31            protocol = c["Protocol"]
32        module_name = "acts.controllers.attenuator_lib.%s.%s" % (attn_model,
33            protocol)
34        module = importlib.import_module(module_name)
35        inst_cnt = c["InstrumentCount"]
36        attn_inst = module.AttenuatorInstrument(inst_cnt)
37        attn_inst.model = attn_model
38        insts = attn_inst.open(c[Config.key_address.value],
39            c[Config.key_port.value])
40        for i in range(inst_cnt):
41            attn = Attenuator(attn_inst, idx=i)
42            if "Paths" in c:
43                try:
44                    setattr(attn, "path", c["Paths"][i])
45                except IndexError:
46                    logger.error("No path specified for attenuator %d." % i)
47                    raise
48            objs.append(attn)
49    return objs
50
51def destroy(objs):
52    return
53
54r"""
55Base classes which define how attenuators should be accessed, managed, and manipulated.
56
57Users will instantiate a specific child class, but almost all operation should be performed
58on the methods and data members defined here in the base classes or the wrapper classes.
59"""
60
61
62class AttenuatorError(Exception):
63    r"""This is the Exception class defined for all errors generated by Attenuator-related modules.
64    """
65    pass
66
67
68class InvalidDataError(AttenuatorError):
69    r"""This exception is  thrown when an unexpected result is seen on the transport layer below
70    the module.
71
72    When this exception is seen, closing an re-opening the link to the attenuator instrument is
73    probably necessary. Something has gone wrong in the transport.
74    """
75    pass
76
77
78class InvalidOperationError(AttenuatorError):
79    r"""Certain methods may only be accessed when the instance upon which they are invoked is in
80    a certain state. This indicates that the object is not in the correct state for a method to be
81    called.
82    """
83    pass
84
85
86class AttenuatorInstrument():
87    r"""This is a base class that defines the primitive behavior of all attenuator
88    instruments.
89
90    The AttenuatorInstrument class is designed to provide a simple low-level interface for
91    accessing any step attenuator instrument comprised of one or more attenuators and a
92    controller. All AttenuatorInstruments should override all the methods below and call
93    AttenuatorInstrument.__init__ in their constructors. Outside of setup/teardown,
94    devices should be accessed via this generic "interface".
95    """
96    model = None
97    INVALID_MAX_ATTEN = 999.9
98
99    def __init__(self, num_atten=0):
100        r"""This is the Constructor for Attenuator Instrument.
101
102        Parameters
103        ----------
104        num_atten : This optional parameter is the number of attenuators contained within the
105        instrument. In some instances setting this number to zero will allow the driver to
106        auto-determine, the number of attenuators; however, this behavior is not guaranteed.
107
108        Raises
109        ------
110        NotImplementedError
111            This constructor should never be called directly. It may only be called by a child.
112
113        Returns
114        -------
115        self
116            Returns a newly constructed AttenuatorInstrument
117        """
118
119        if type(self) is AttenuatorInstrument:
120            raise NotImplementedError("Base class should not be instantiated directly!")
121
122        self.num_atten = num_atten
123        self.max_atten = AttenuatorInstrument.INVALID_MAX_ATTEN
124        self.properties = None
125
126    def set_atten(self, idx, value):
127        r"""This function sets the attenuation of an attenuator given its index in the instrument.
128
129        Parameters
130        ----------
131        idx : This zero-based index is the identifier for a particular attenuator in an
132        instrument.
133        value : This is a floating point value for nominal attenuation to be set.
134
135        Raises
136        ------
137        NotImplementedError
138            This constructor should never be called directly. It may only be called by a child.
139        """
140        raise NotImplementedError("Base class should not be called directly!")
141
142    def get_atten(self, idx):
143        r"""This function returns the current attenuation from an attenuator at a given index in
144        the instrument.
145
146        Parameters
147        ----------
148        idx : This zero-based index is the identifier for a particular attenuator in an instrument.
149
150        Raises
151        ------
152        NotImplementedError
153            This constructor should never be called directly. It may only be called by a child.
154
155        Returns
156        -------
157        float
158            Returns a the current attenuation value
159        """
160        raise NotImplementedError("Base class should not be called directly!")
161
162
163class Attenuator():
164    r"""This class defines an object representing a single attenuator in a remote instrument.
165
166    A user wishing to abstract the mapping of attenuators to physical instruments should use this
167    class, which provides an object that obscures the physical implementation an allows the user
168    to think only of attenuators regardless of their location.
169    """
170
171    def __init__(self, instrument, idx=0, offset=0):
172        r"""This is the constructor for Attenuator
173
174        Parameters
175        ----------
176        instrument : Reference to an AttenuatorInstrument on which the Attenuator resides
177        idx : This zero-based index is the identifier for a particular attenuator in an instrument.
178        offset : A power offset value for the attenuator to be used when performing future
179        operations. This could be used for either calibration or to allow group operations with
180        offsets between various attenuators.
181
182        Raises
183        ------
184        TypeError
185            Requires a valid AttenuatorInstrument to be passed in.
186        IndexError
187            The index of the attenuator in the AttenuatorInstrument must be within the valid range.
188
189        Returns
190        -------
191        self
192            Returns a newly constructed Attenuator
193        """
194        if not isinstance(instrument, AttenuatorInstrument):
195            raise TypeError("Must provide an Attenuator Instrument Ref")
196        self.model = instrument.model
197        self.instrument = instrument
198        self.idx = idx
199        self.offset = offset
200
201        if(self.idx >= instrument.num_atten):
202            raise IndexError("Attenuator index out of range for attenuator instrument")
203
204    def set_atten(self, value):
205        r"""This function sets the attenuation of Attenuator.
206
207        Parameters
208        ----------
209        value : This is a floating point value for nominal attenuation to be set.
210
211        Raises
212        ------
213        ValueError
214            The requested set value+offset must be less than the maximum value.
215        """
216
217        if value+self.offset > self.instrument.max_atten:
218            raise ValueError("Attenuator Value+Offset greater than Max Attenuation!")
219
220        self.instrument.set_atten(self.idx, value+self.offset)
221
222    def get_atten(self):
223        r"""This function returns the current attenuation setting of Attenuator, normalized by
224        the set offset.
225
226        Returns
227        -------
228        float
229            Returns a the current attenuation value
230        """
231
232        return self.instrument.get_atten(self.idx) - self.offset
233
234    def get_max_atten(self):
235        r"""This function returns the max attenuation setting of Attenuator, normalized by
236        the set offset.
237
238        Returns
239        -------
240        float
241            Returns a the max attenuation value
242        """
243        if (self.instrument.max_atten == AttenuatorInstrument.INVALID_MAX_ATTEN):
244            raise ValueError("Invalid Max Attenuator Value")
245
246        return self.instrument.max_atten - self.offset
247
248
249class AttenuatorGroup(object):
250    r"""This is a handy abstraction for groups of attenuators that will share behavior.
251
252    Attenuator groups are intended to further facilitate abstraction of testing functions from
253    the physical objects underlying them. By adding attenuators to a group, it is possible to
254    operate on functional groups that can be thought of in a common manner in the test. This
255    class is intended to provide convenience to the user and avoid re-implementation of helper
256    functions and small loops scattered throughout user code.
257
258    """
259
260    def __init__(self, name=""):
261        r"""This is the constructor for AttenuatorGroup
262
263        Parameters
264        ----------
265        name : The name is an optional parameter intended to further facilitate the passing of
266        easily tracked groups of attenuators throughout code. It is left to the user to use the
267        name in a way that meets their needs.
268
269        Returns
270        -------
271        self
272            Returns a newly constructed AttenuatorGroup
273        """
274        self.name = name
275        self.attens = []
276        self._value = 0
277
278    def add_from_instrument(self, instrument, indices):
279        r"""This function provides a way to create groups directly from the Attenuator Instrument.
280
281        This function will create Attenuator objects for all of the indices passed in and add
282        them to the group.
283
284        Parameters
285        ----------
286        instrument : A ref to the instrument from which attenuators will be added
287        indices : You pay pass in the indices either as a range, a list, or a single integer.
288
289        Raises
290        ------
291        TypeError
292            Requires a valid AttenuatorInstrument to be passed in.
293        """
294
295        if not instrument or not isinstance(instrument, AttenuatorInstrument):
296            raise TypeError("Must provide an Attenuator Instrument Ref")
297
298        if type(indices) is range or type(indices) is list:
299            for i in indices:
300                self.attens.append(Attenuator(instrument, i))
301        elif type(indices) is int:
302            self.attens.append(Attenuator(instrument, indices))
303
304    def add(self, attenuator):
305        r"""This function adds an already constructed Attenuator object to the AttenuatorGroup.
306
307        Parameters
308        ----------
309        attenuator : An Attenuator object.
310
311        Raises
312        ------
313        TypeError
314            Requires a valid Attenuator to be passed in.
315        """
316
317        if not isinstance(attenuator, Attenuator):
318            raise TypeError("Must provide an Attenuator")
319
320        self.attens.append(attenuator)
321
322    def synchronize(self):
323        r"""This function can be called to ensure all Attenuators within a group are set
324        appropriately.
325        """
326
327        self.set_atten(self._value)
328
329    def is_synchronized(self):
330        r"""This function queries all the Attenuators in the group to determine whether or not
331        they are synchronized.
332
333        Returns
334        -------
335        bool
336            True if the attenuators are synchronized.
337        """
338
339        for att in self.attens:
340            if att.get_atten() != self._value:
341                return False
342        return True
343
344    def set_atten(self, value):
345        r"""This function sets the attenuation value of all attenuators in the group.
346
347        Parameters
348        ----------
349        value : This is a floating point value for nominal attenuation to be set.
350
351        Returns
352        -------
353        bool
354            True if the attenuators are synchronized.
355        """
356
357        value = float(value)
358        for att in self.attens:
359            att.set_atten(value)
360        self._value = value
361
362    def get_atten(self):
363        r"""This function returns the current attenuation setting of AttenuatorGroup.
364
365        This returns a cached value that assumes the attenuators are synchronized. It avoids a
366        relatively expensive call for a common operation, and trusts the user to ensure
367        synchronization.
368
369        Returns
370        -------
371        float
372            Returns a the current attenuation value for the group, which is independent of any
373            individual attenuator offsets.
374        """
375
376        return float(self._value)
377