[docs]classScaledParameter(Parameter):""" :class:`.Parameter` Scaler To be used when you use a physical voltage divider or an amplifier to set or get a quantity. Initialize the parameter by passing the parameter to be measured/set and the value of the division OR the gain. The scaling value can be either a scalar value or a Qcodes Parameter. The parameter scaler acts a your original parameter, but will set the right value, and store the gain/division in the metadata. Examples: Resistive voltage divider >>> vd = ScaledParameter(dac.chan0, division = 10) Voltage multiplier >>> vb = ScaledParameter(dac.chan0, gain = 30, name = 'Vb') Transimpedance amplifier >>> Id = ScaledParameter(multimeter.amplitude, ... division = 1e6, name = 'Id', unit = 'A') Args: output: Physical Parameter that need conversion. division: The division value. gain: The gain value. label: Label of this parameter, by default uses 'output' label but attaches _amplified or _attenuated depending if gain or division has been specified. name: Name of this parameter, by default uses 'output' name but attaches _amplified or _attenuated depending if gain or division has been specified. unit: Resulting unit. It uses the one of 'output' by default. """
def__init__(self,output:Parameter,division:float|Parameter|None=None,gain:float|Parameter|None=None,name:str|None=None,label:str|None=None,unit:str|None=None,)->None:# Set labeliflabel:self.label=labelelifname:self.label=nameelse:self.label=f"{output.label}_scaled"# Set the nameifnotname:name=f"{output.name}_scaled"# Set the unitifunit:self.unit=unitelse:self.unit=output.unitsuper().__init__(name=name,label=self.label,unit=self.unit)self._wrapped_parameter=outputself._wrapped_instrument=getattr(output,"_instrument",None)# Set the role, either as divider or amplifier# Raise an error if nothing is specifiedis_divider=divisionisnotNoneis_amplifier=gainisnotNoneifnotxor(is_divider,is_amplifier):raiseValueError("Provide only division OR gain")ifdivisionisnotNone:self.role=ScaledParameter.Role.DIVISION# Unfortunately mypy does not support# properties where the setter has different types than# the actual property. We use that here to cast different inputs# to the same type.# https://github.com/python/mypy/issues/3004self._multiplier=division# type: ignore[assignment]elifgainisnotNone:self.role=ScaledParameter.Role.GAINself._multiplier=gain# type: ignore[assignment]# extend metadataself._meta_attrs.extend(["division"])self._meta_attrs.extend(["gain"])self._meta_attrs.extend(["role"])self.metadata["wrapped_parameter"]=self._wrapped_parameter.nameifself._wrapped_instrument:wrapped_instr_name=getattr(self._wrapped_instrument,"name",None)self.metadata["wrapped_instrument"]=wrapped_instr_name# Internal handling of the multiplier# can be either a Parameter or a scalar@propertydef_multiplier(self)->Parameter:ifself._multiplier_parameterisNone:raiseRuntimeError("Cannot get multiplier when multiplier parameter in unknown.")returnself._multiplier_parameter@_multiplier.setterdef_multiplier(self,multiplier:float|Parameter)->None:ifisinstance(multiplier,Parameter):self._multiplier_parameter=multipliermultiplier_name=self._multiplier_parameter.nameself.metadata["variable_multiplier"]=multiplier_nameelse:self._multiplier_parameter=ManualParameter("multiplier",initial_value=multiplier)self.metadata["variable_multiplier"]=False# Division of the scaler@propertydefdivision(self)->float:value=cast(float,self._multiplier())ifself.role==ScaledParameter.Role.DIVISION:returnvalueelifself.role==ScaledParameter.Role.GAIN:return1/valueelse:raiseValueError(f"Unexpected role {self.role}")@division.setterdefdivision(self,division:float|Parameter)->None:self.role=ScaledParameter.Role.DIVISIONself._multiplier=division# type: ignore[assignment]# Gain of the scaler@propertydefgain(self)->float:value=cast(float,self._multiplier())ifself.role==ScaledParameter.Role.GAIN:returnvalueelifself.role==ScaledParameter.Role.DIVISION:return1/valueelse:raiseValueError(f"Unexpected role {self.role}")@gain.setterdefgain(self,gain:float|Parameter)->None:self.role=ScaledParameter.Role.GAINself._multiplier=gain# type: ignore[assignment]# Getter and setter for the real value
[docs]defget_raw(self)->float:""" Returns: value at which was set at the sample """wrapped_value=cast(float,self._wrapped_parameter())multiplier=cast(float,self._multiplier())ifself.role==ScaledParameter.Role.GAIN:value=wrapped_value*multiplierelifself.role==ScaledParameter.Role.DIVISION:value=wrapped_value/multiplierelse:raiseRuntimeError(f"ScaledParameter must be either a"f"Multiplier or Divisor; got {self.role}")returnvalue
@propertydefwrapped_parameter(self)->Parameter:""" The attached unscaled parameter """returnself._wrapped_parameter
[docs]defget_wrapped_parameter_value(self)->float:""" Returns: value at which the attached parameter is (i.e. does not account for the scaling) """returnself._wrapped_parameter.get()
[docs]defset_raw(self,value:float)->None:""" Set the value on the wrapped parameter, accounting for the scaling """multiplier_value=cast(float,self._multiplier())ifself.role==ScaledParameter.Role.GAIN:instrument_value=value/multiplier_valueelifself.role==ScaledParameter.Role.DIVISION:instrument_value=value*multiplier_valueelse:raiseRuntimeError(f"ScaledParameter must be either a"f"Multiplier or Divisor; got {self.role}")self._wrapped_parameter.set(instrument_value)