Source code for rfblocks.pe43711

# `pe43711` - Encapsulates control of the PE43711 step attenuator.
#
#    Copyright (C) 2020 Dyadic Pty Ltd
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU Lesser General Public License as published
#    by the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU Lesser General Public License for more details.
#
#    You should have received a copy of the GNU Lesser General Public License
#    along with this program.  If not, see <https://www.gnu.org/licenses/>.

from typing import (
    Optional, Callable
)


[docs]class AttenuationRangeException(Exception): """Raised when a requested attenuation is out of range. """ pass
[docs]class pe43711(object): """Encapsulates control of the PE43711 step attenuator. Documentation for the PE43711 step attenuator rfblocks module can be found here: `A PE43711 Step Attenuator <../boards/PE43711-Step-Attenuator.html>`_ """ MIN_ATTENUATION = 0.0 MAX_ATTENUATION = 31.75 STEP_SIZE = 0.25
[docs] @classmethod def default_insertion_loss(cls, freq: float) -> float: """Calculates attenuator default insertion loss. By default the attenuator insertion loss is modelled as a linear function. The slope and intercept of the function are derived from VNA measurements of the attenuator S21 with an attenuation setting of 0 dB. :param freq: Frequency (in MHz) for computing the insertion loss. :type freq: float :return: The attenuator insertion loss (in dB) for an attenuation setting of 0 dB at the specified frequency. """ DEFAULT_INSLOSS_INTERCEPT = -1.17 DEFAULT_INSLOSS_SLOPE = -3.29e-4 return DEFAULT_INSLOSS_INTERCEPT + (DEFAULT_INSLOSS_SLOPE * freq)
def __init__(self, le: str = None) -> None: """ :param le: The PE43711 latch enable (`LE`) controller pin :type le: str """ self.le = le.upper() if le else le self._insertion_loss_fn = pe43711.default_insertion_loss def __str__(self) -> str: """ """ return 'PE43711: le: {}'.format(self.le) def __repr__(self): return self.__str__()
[docs] def pin_config(self) -> str: """Initialize controller pin configuration. :return: A string specifying the commands required to initialize the connected controller pins. """ cmd = '' if self.le: cmd += 'O{}:L{}:'.format(self.le, self.le) return cmd
[docs] def chip_reset(self) -> str: """Reset the chip internal logic to default states. :return: A string containing the controller commands required to reset the chip. """ return ""
[docs] def atten_word(self, attn_db: float) -> str: """Calculate the attenuation word for a specified attenuation level. :param attn_db: The desired attenuation level in dB. This should be in the range 0 to 31.75 dB. :type attn_db: float :return: An 8-bit integer containing the attenuation 'word'. :raises: AttenuationRangeException If the specified attenuation is out of range. """ if attn_db < 0 or attn_db > 31.75: raise AttenuationRangeException else: n = int(round(attn_db * 4)) # Reverse the bit order. PE43711 wants the atten. setting # least significant bit first. return int('{:08b}'.format(n)[::-1], 2)
[docs] def config_atten_word(self, attn_word: int) -> str: """Update the current attenuation word. :param attn_word: The attenuator word for the desired attenuation. Note that only the lower 7 bits of the value are used. :type attn_word: 8-bit integer :return: The command string required to update the current device attentuation value. """ cmd = '' if attn_word is not None: attn_word = attn_word astr = '{:02X}'.format(attn_word) cmd = 'W{}:H{}:L{}:'.format(astr[0:2], self.le, self.le) return cmd
[docs] def insertion_loss(self, freq: float) -> float: """Calculate the attenuator insertion loss. Insertion loss is computed using a 0 dB attenator setting. :param freq: Frequency, in MHz, at which to compute the insertion loss. :type freq: float :return: The insertion loss (in dB) at the specified frequency. """ return self._insertion_loss_fn(freq)
[docs] def set_insertion_loss_fn(self, fn: Optional[Callable[[float], float]]) -> None: """Set a callable to use when calculating the attenuator insertion loss. :param fn: A callable which takes a single float parameter specifying the frequency in MHz and returning the insertion loss in dB. """ self._insertion_loss_fn = fn
if __name__ == '__main__': att1 = pe43711('d1') print(' att1: {}'.format(att1)) print("att1 pin conf: {}".format(att1.pin_config())) print("test set attenuation:") print(" 10dB: {}".format(att1.config_atten_word(att1.atten_word(10.0)))) print(" 20dB: {}".format(att1.config_atten_word(att1.atten_word(20.0)))) print(" 30dB: {}".format(att1.config_atten_word(att1.atten_word(30.0)))) print(" 0dB: {}".format(att1.config_atten_word(att1.atten_word(0.0))))