Beispiel #1
0
 def power_unit(self, unit):
     if isinstance(unit, str):
         unit = unit.upper()
         unit = strict_discrete_set(unit, ('W', 'DBM'))
         unit = {'W': 0, 'DBM': 1}[unit]
     unit = strict_discrete_set(int(unit), (0, 1))
     self.dll('set{}PowerUnit'.format(self.meas_type), c_int16(unit))
Beispiel #2
0
 def __init__(self, instrument):
     super().__init__(instrument, command_prefix="POWer")
     self.unit = Instrument.control(
         self._cmd("UNIT?"), self._cmd("UNIT %s"),
         "Power Unit setting, W or dBm",
         validator=lambda v: strict_discrete_set(v, ("W", "dBm", "dbm"))
         )
Beispiel #3
0
 def home(self, type=1):
     """ Drives the axis to the home position, which may be the negative
     hardware limit for some actuators (e.g. LTA-HS).
     type can take integer values from 0 to 6.
     """
     home_type = strict_discrete_set(type, [0,1,2,3,4,5,6])
     self.write("OR%d" % home_type)
Beispiel #4
0
 def home(self, type=1):
     """ Drives the axis to the home position, which may be the negative
     hardware limit for some actuators (e.g. LTA-HS).
     type can take integer values from 0 to 6.
     """
     home_type = strict_discrete_set(type, [0, 1, 2, 3, 4, 5, 6])
     self.write("OR%d" % home_type)
Beispiel #5
0
 def range(self, value):
     if isinstance(value, str):
         value = value.upper()
         value = strict_discrete_set(
             value, ('MIN', 'MAX', 'min', 'max'))
     else:
         value = strict_range(value, (self.range_min, self.range_max))
         value = "%g" % value
     self.write(self._cmd("RANGe %s" % value))
Beispiel #6
0
 def reference(self, value):
     if isinstance(value, str):
         value = value.upper()
         value = strict_discrete_set(
             value, ('MIN', 'MAX', 'DEF', 'DEFAULT'))
     else:
         value = strict_range(
             value, (self.reference_min, self.reference_max))
         value = "%g" % value
     self.write(self._cmd("REFerence %s" % value))
Beispiel #7
0
    def configure_frequency_array_measurement(self, n_samples, channel):
        """
        Configure the counter for an array of measurements.

        :param n_samples: The number of samples
        :param channel: Measurment channel (A, B, C, E, INTREF)
        """
        n_samples = truncated_range(n_samples, [1, self.MAX_BUFFER_SIZE])
        channel = strict_discrete_set(channel, self.CHANNELS)
        channel = self.CHANNELS[channel]
        self.write(f":CONF:ARR:FREQ {n_samples},(@{channel})")
Beispiel #8
0
    def __init__(self, RTO_channel, waveform_index):
        """Waveform specific properties of RTO channel

        :param RTO_channel: RTO_channel instance
        :type RTO_channel: `class:RTO_channel`
        :param waveform_index: index of waveform
        :type waveform_index: int
        """
        self.RTO_channel = RTO_channel
        waveform_index = strict_discrete_set(waveform_index, (1, 2, 3))
        self.waveform_index = waveform_index
        self.waveform = "waveform%d" % self.waveform_index
Beispiel #9
0
 def input_adapter_type(self, sensor_type):
     if isinstance(sensor_type, str):
         if 'PHOTODIODE' in sensor_type.upper():
             sensor_type = 1
         elif 'THERMOPILE' in sensor_type.upper():
             sensor_type = 2
         elif 'PYROELECTRIC' in sensor_type.upper():
             sensor_type = 3
         else:
             log.warning('Sensor type {} not known.'.format(sensor_type))
             return
     sensor_type = strict_discrete_set(sensor_type, (1, 2, 3))
     self.dll('setInputAdapterType', c_int16(sensor_type))
Beispiel #10
0
    def signal_bitmask(enable_list):
        """Generate bitmask (integer value) from list of booleans.

        :param enable_list: either 6 (LEDs only) or 12 (LEDs + AUX) elements
        """
        if len(enable_list) not in (6, 12):
            raise ValueError(
                'List of channels must have 6 or 12 entries.')
        enable_list = [strict_discrete_set(v, (True, False, 0, 1))
                       for v in enable_list]
        bitmask = 0
        for i, value in enumerate(enable_list):
            if bool(value) is True:
                bitmask += 2**i
        return bitmask
Beispiel #11
0
    def calibration(self, number=1, subsystem=None):
        """
        Function to either calibrate the whole modulator, when subsystem parameter is omitted,
        or calibrate a subsystem of the modulator.

        Valid subsystem selections: "NICam, VISion, SOUNd1, SOUNd2, CODer"

        """
        if subsystem is None:
            self.write("CAL:MOD%d" % (number))
        else:
            self.write("CAL:MOD%d:%s" %
                       (number,
                        strict_discrete_set(subsystem, [
                            "NIC", "NICAM", "VIS", "VISION", "SOUN1", "SOUND1",
                            "SOUN2", "SOUND2", "COD", "CODER"
                        ])))
Beispiel #12
0
    def _preset_min_max_values(self, property_name, value, default=True):
        """Allows to pass `'MIN'/'MAX'/'DEFAULT'` as
        value specification for a property.
        Checks if value is within range.
        Returns numerical value to pass to instrument.

        :param property_name: name of property
        :type property_name: str
        :param value: value to set
        :type value: str or numeric
        :return: numerical value
        """
        if isinstance(value, str):
            allowed = ('min', 'max')
            if default:
                allowed += ('default', )
            value = strict_discrete_set(value.lower(), allowed)
            value = getattr(self, '{}_{}'.format(property_name, value))
        value = strict_range(value,
                             (getattr(self, '{}_min'.format(property_name)),
                              getattr(self, '{}_max'.format(property_name))))
        return value
Beispiel #13
0
    def _acquire_multi_waveform(self, setting):
        setting = strict_discrete_set(setting, (True, False))
        if setting == self._acquire_multi_waveform:
            # no changes!
            return

        # delete existing waveform attributes
        for wfm in self.waveforms:
            del (wfm)
        if setting:
            # multiple waveforms
            waveforms = []
            for i in (1, 2, 3):
                setattr(self, "waveform%d" % i, RTO_channel_waveform(self, i))
                waveforms.append(getattr(self, "waveform%d" % i))
            self.waveforms = waveforms
        else:
            # single waveform = waveform1
            waveforms = []
            setattr(self, "waveform1", RTO_channel_waveform(self, 1))
            waveforms.append(getattr(self, "waveform1"))
            self.waveforms = waveforms
        self._multi_waveform = setting
Beispiel #14
0
class Agilent4024A(Instrument):
    """ Represents the Agilent4024A Digital sampling oscilloscope
    and provides a high-level for interacting with the instrument

    .. code-block:: python

        osc= TDS620B("GPIB0::...")
        osc.timebase_reference = 'LEFT' # set pretrigger to 10% of recorded waveform
        osc.acquire_mode = 'RTIMe' # the default
        osc.acquire_stop_after = 'SEQUENCE' # Stop recording after 1 waveform after trigger
        osc.data_source = 'CH1' # set data source to channel 1
        #emit trigger somehow
        data = osc.get_binary_curve() # get curve data from channel 1 with default binary encoding

    """
    timebase_reference = Instrument.control(
        ":TIMebase:REFerence?",
        ":TIMebase:REFerence %s",
        """A string control that sets the timebase reference location. Also called 'pretrigger' on other scopes.
        Options are 'LEFT', 1 div from left; 'CENT',5 divs from left and right; 'RIGH', 1 div from the right; 
        and 'CUST', where the position is set by timebase_reference_loc.""",
        validator=strict_discrete_set(),
        values=['LEFT', 'RIGH', 'CENT', 'CUST'])

    timebase_reference_loc = Instrument.control(
        ":TIMebase:REFerence:LOCation?",
        ":TIMebase:REFerence CUST;:TIMebase:REFerence:LOCation %g",
        """A decimal property that explicitly sets the timebase reference location (also called pretrigger).
        Can take any value between 0 (trigger is left of record) to 1 (trigger is right of record).
        Sets timebase_reference to custom to produce expected behavior.""",
        validator=strict_range,
        values=[0, 1])

    acquire_mode = Instrument.control(
        ":ACQuire:MODe?",
        ":ACQuire:MODe %s",
        """A string property that controls how data are acquired. Can be set to:
        ['RTIMe', 'ETIMe', 'SEGmented']""",
        validator=string_validator,
        values=['RTIMe', 'ETIMe', 'SEGmented'])

    acquire_naverages = Instrument.control(
        "ACQuire:NUMAVg?",
        "ACQuire:NUMAVg %d",
        """A integer property ranging from 2 to 10,000 that controls the number
        of averages taken if the acquire_mode is AVERAGE.""",
        validator=strict_range,
        values=[2, 10000])

    acquire_num_acq = Instrument.measurement(
        "ACQuire:NUMACq?",
        """A integer property indicating the number of acquisitions that have taken
        place since starting acquisition.""",
    )

    acquire_state = Instrument.control(
        "ACQuire:STATE?",
        "ACQuire:STATE %s",
        """A string property that starts or stops acquisitions. Equivalent to pressing
         RUN/STOP button on front panel. Can be set to:
        RUN,STOP""",
        validator=string_validator,
        values=['RUN', 'STOP'])

    acquire_stop_after = Instrument.control(
        "ACQuire:STOPAfter?",
        "ACQuire:STOPAfter %s",
        """A string property that sets when to stop taking acquisitions. Can be set to:
        RUNSTOP, SEQUENCE, LIMIT. RUNSTOP -> run until front panel run/stop is pressed,
        SEQUENCE -> stop after collecting trace following a trigger event.
        LIMIT-> stop after limit test condition is met.""",
        validator=string_validator,
        values=['RUNSTOP', 'SEQUENCE', 'LIMIT'])

    data = Instrument.control(
        "DATa?",
        "DATa: %s",
        """A string property that sets the data record start and stop positions through keywords. 
        Options are 'INIT' to return to factory default, and 'SNAP' to snap to vertical cursors""",
        validator=string_validator,
        values=['INIT', 'SNAP'])

    data_start = Instrument.control(
        "DATa:STARt?",
        "DATa:STARt %d",
        """An integer proper that sets the record start point""",
        validator=truncated_range,
        values=[0, 5000])

    data_stop = Instrument.control(
        "DATa:STOP?",
        "DATa:STOP %d",
        """An interger property that sets the data record stop""",
        validator=truncated_range,
        values=[0, 5000])

    data_encoding = Instrument.control(
        "DATa:ENCdg?",
        "DATa:ENCdg %s",
        """A string property that sets the data encoding for transfer. 
        Options are 'RIPBINARY', 'RPBINARY', 'SRIBINARY', 'SRPBINARY'. The 'SRIBINARY' is the default, tested, value
         for the defaults of pymeasure's get_binary_values""",
        validator=string_validator,
        values=['ASCII', 'RIPBINARY', 'RPBINARY', 'SRIBINARY', 'SRPBINARY'])

    data_source = Instrument.control(
        "DATa:SOUrce?",
        "DATa:SOUrce %s",
        """A string property that sets the data source for transfer. """,
        validator=string_validator,
        values=['CH1', 'CH2', 'CH3', 'CH4', 'MATH1', 'MATH2', 'MATH3'])

    data_width = Instrument.control(
        "DATa:WIDth?",
        "DATa:WIDth %d",
        """A string property that sets the data source for transfer. """,
        validator=strict_discrete_set,
        values=[1, 2])

    trigger_details = Instrument.measurement(
        "TRIGGER?",
        """Outputs a semicolon-delimited list of the current trigger parameters of the scope""",
    )

    trigger_main_details = Instrument.measurement(
        "TRIGGER:MAIn:EDGE?",
        """Outputs a semicolon-delimited list of the main trigger parameters of the scope""",
    )

    trigger_main_edge_coupling = Instrument.control(
        "TRIGger:MAIn:EDGE:COUPling?",
        "TRIGger:MAIn:EDGE:COUPling %s",
        """A string property that sets the main coupling. Options are: 'AC', 'DC', 'HFRej', 'LFRej', 'NOISErej' """,
        validator=string_validator,
        values=['AC', 'DC', 'HFRej', 'LFRej', 'NOISErej'])

    trigger_main_edge_slope = Instrument.control(
        "TRIGger:MAIn:EDGE:SLOpe?",
        "TRIGger:MAIn:EDGE:SLOpe %s",
        """A string property that sets the trigger slope to look at the rising ('RISe') or falling ('FALL') edge  """,
        validator=string_validator,
        values=['FALL', 'RISe'])

    trigger_main_edge_source = Instrument.control(
        "TRIGger:MAIn:EDGE:SOUrce?",
        "TRIGger:MAIn:EDGE:SOUrce %s",
        """A string property that sets the main trigger source. Options are 'CH1', 'CH2', 'CH3', 'CH4', 'LINE' """,
        validator=string_validator,
        values=['CH1', 'CH2', 'CH3', 'CH4', 'LINE'])

    trigger_main_holdoff_time = Instrument.control(
        "TRIGger:MAIn:HOLDOff:TIMe?",
        "TRIGger:MAIn:HOLDOff:TIMe %g",
        """A float property setting the holdoff time in seconds. Limits are 250 ns to 12 s.
          """,
        validator=truncated_range,
        values=[250e-9, 12])

    trigger_main_level = Instrument.control(
        "TRIGger:MAIn:LEVel?",
        "TRIGger:MAIn:LEVel %g",
        """A float property that sets the main trigger level. """,
        validator=truncated_range,
        values=[-10.0, 10.0])

    trigger_main_type = Instrument.control(
        "TRIGger:MAIn:TYPe?",
        "TRIGger:MAIn:TYPe %s",
        """A string property that sets the main trigger type. 'EDGE' is the normal, classic trigger. 
         PULse and LOGIc are also included but untested.""",
        validator=string_validator,
        values=['EDGE', 'LOGIc', 'PULse'])

    horizontal_secperdiv = Instrument.control(
        "HORizontal:SECdiv?",
        "HORizontal:SECdiv %g",
        """A float property that sets seconds per division. Ranges from 200 ps/div to 10 s/div,
        range is not continuous, and I was too lazy to figure out the values. The scope picks the next smallest
        sec/div if you specify one that is not a allowed.""",
        validator=truncated_range,
        values=[.2e-9, 10])

    horizontal_recordlength = Instrument.control(
        "HORizontal:RECOrdlength?",
        "HORizontal:RECOrdlength %d",
        """An integer property that sets the length of the waveform record. Values are 500,1000,2500,5000,15000 points.
         Longer records will extend past the end of the scope screen and take longer to transfer. 
         There are 50 points/div""",
        validator=strict_discrete_set,
        values=[500, 1000, 2000, 2500, 5000, 15000])

    CH1 = Instrument.measurement(
        "CH1?",
        """A property that returns all vertical paramters associated with CH1""",
    )

    CH1_bandwidth = Instrument.control("CH1:BANdwidth?",
                                       "CH1:BANdwidth %s",
                                       """
        String parameter that sets the channel bandwidth to 'TWEnty':20 MHz, 'TWOfifty':250 MHz, 'FULl': 500 MHz.
        """,
                                       validator=string_validator,
                                       values=['TWEnty', 'TWOfifty', 'FULl'])

    CH1_coupling = Instrument.control("CH1:COUPling?",
                                      "CH1:COUPling %s",
                                      """
        String parameter that sets the channel coupling to 'AC', 'DC', 'GND'.
        """,
                                      validator=string_validator,
                                      values=['AC', 'DC', 'GND'])

    CH1_deskew = Instrument.control("CH1:DESKew?",
                                    "CH1:DESKew %g",
                                    """
        Float parameter in range -25 ns to +25 ns with resolution of 1 ps. Used to compensate for cables of different
        lengths. Implemented for completeness, read about before using.
        """,
                                    validator=truncated_range,
                                    values=[-25e-9, 25e-9])

    CH1_impedance = Instrument.control("CH1:IMPedance?",
                                       "CH1:IMPedance %s",
                                       """
        String parameter that sets the channel impedance to  'FIFty', 'MEG'.
        """,
                                       validator=string_validator,
                                       values=['FIFty', 'MEG'])

    CH1_offset = Instrument.control("CH1:OFFSet?",
                                    "CH1:OFFSet %g",
                                    """
        Float parameter that sets the channel offset. There are restrictions based on V/div
        """,
                                    validator=truncated_range,
                                    values=[-10, 10])

    CH1_scale = Instrument.control("CH1:SCAle?",
                                   "CH1:SCAle %g",
                                   """
        Float parameter that sets the channel scale (V/div), range is 100 mV to 1 mV
        """,
                                   validator=truncated_range,
                                   values=[1e-3, 1e-1])

    CH2 = Instrument.measurement(
        "CH2?",
        """A property that returns all vertical paramters associated with CH2""",
    )

    CH2_bandwidth = Instrument.control("CH2:BANdwidth?",
                                       "CH2:BANdwidth %s",
                                       """
        String parameter that sets the channel bandwidth to 'TWEnty':20 MHz, 'TWOfifty':250 MHz, 'FULl': 500 MHz.
        """,
                                       validator=string_validator,
                                       values=['TWEnty', 'TWOfifty', 'FULl'])

    CH2_coupling = Instrument.control("CH2:COUPling?",
                                      "CH2:COUPling %s",
                                      """
        String parameter that sets the channel coupling to 'AC', 'DC', 'GND'.
        """,
                                      validator=string_validator,
                                      values=['AC', 'DC', 'GND'])

    CH2_deskew = Instrument.control("CH2:DESKew?",
                                    "CH2:DESKew %g",
                                    """
        Float parameter in range -25 ns to +25 ns with resolution of 1 ps. Used to compensate for cables of different
        lengths. Implemented for completeness, read about before using.
        """,
                                    validator=truncated_range,
                                    values=[-25e-9, 25e-9])

    CH2_impedance = Instrument.control("CH2:IMPedance?",
                                       "CH2:IMPedance %s",
                                       """
        String parameter that sets the channel impedance to  'FIFty', 'MEG'.
        """,
                                       validator=string_validator,
                                       values=['FIFty', 'MEG'])

    CH2_offset = Instrument.control("CH2:OFFSet?",
                                    "CH2:OFFSet %g",
                                    """
        Float parameter that sets the channel offset. There are restrictions based on V/div
        """,
                                    validator=truncated_range,
                                    values=[-10, 10])

    CH2_scale = Instrument.control("CH2:SCAle?",
                                   "CH2:SCAle %g",
                                   """
        Float parameter that sets the channel scale (V/div), range is 100 mV to 1 mV
        """,
                                   validator=truncated_range,
                                   values=[1e-3, 1e-1])

    def force_trigger(self):
        """
        Forces a trigger event to occur
        """
        self.write('TRIGGER FORCe')

    def get_waveform_parameters(self, source):
        """
        Get the waveform parameters for a given waveform source.
        """
        self.data_source = source
        return self.ask('WFMPre:' + source + '?')

    def get_binary_curve(self,
                         source='CH1',
                         encoding='SRIBINARY',
                         hires=False):
        """
        Convenience function to get the curve data from the sources available in self.data_source using binary encoding.
        Transfer is signed, 0 is in center. 5 divs above and below recorded (though only +-4 shown on scope)
        If trigger is unchanged then pulse is starts being measured at 50% of time scale, so halfway
        """
        self.data_encoding = encoding
        self.data_source = source
        if not hires:
            self.data_width = 1
            out = self.binary_values('CURVe?', dtype=np.int8)
        else:
            self.data_width = 2
            out = self.binary_values('CURVe?', dtype=np.int16)

        return out

    def get_ascii_curve(self, source='CH1', hires=False):
        """
        Function to get the curve data from the sources available in self.data_source using ascii encoding.
        """
        self.data_encoding = 'ASCII'
        self.data_source = source
        if not hires:
            self.data_width = 1
        else:
            self.data_width = 2
        out = self.ask('CURVe?')
        return out

    class Measurement(object):

        SOURCE_VALUES = ['CH1', 'CH2', 'MATH']

        TYPE_VALUES = [
            'FREQ', 'MEAN', 'PERI', 'PHA', 'PK2', 'CRM', 'MINI', 'MAXI', 'RIS',
            'FALL', 'PWI', 'NWI'
        ]

        UNIT_VALUES = ['V', 's', 'Hz']

        def __init__(self, parent, preamble="MEASU:IMM:"):
            self.parent = parent
            self.preamble = preamble

        @property
        def value(self):
            return self.parent.values("%sVAL?" % self.preamble)

        @property
        def source(self):
            return self.parent.ask("%sSOU?" % self.preamble).strip()

        @source.setter
        def source(self, value):
            if value in TDS620B.Measurement.SOURCE_VALUES:
                self.parent.write("%sSOU %s" % (self.preamble, value))
            else:
                raise ValueError("Invalid source ('%s') provided to %s" %
                                 (self.parent, value))

        @property
        def type(self):
            return self.parent.ask("%sTYP?" % self.preamble).strip()

        @type.setter
        def type(self, value):
            if value in TDS620B.Measurement.TYPE_VALUES:
                self.parent.write("%sTYP %s" % (self.preamble, value))
            else:
                raise ValueError("Invalid type ('%s') provided to %s" %
                                 (self.parent, value))

        @property
        def unit(self):
            return self.parent.ask("%sUNI?" % self.preamble).strip()

        @unit.setter
        def unit(self, value):
            if value in TDS620B.Measurement.UNIT_VALUES:
                self.parent.write("%sUNI %s" % (self.preamble, value))
            else:
                raise ValueError("Invalid unit ('%s') provided to %s" %
                                 (self.parent, value))

    def __init__(self, resourceName, **kwargs):
        super(TDS620B,
              self).__init__(resourceName, "Tektronix TDS 620B Oscilliscope",
                             **kwargs)
        self.measurement = TDS620B.Measurement(self)
Beispiel #15
0
 def trigger(self, value):
     trig_set = self.TRIGGERS[strict_discrete_set(value, self.TRIGGERS)]
     self.write(trig_set)
Beispiel #16
0
 def resolution(self, value):
     resolution_string = "N" + str(strict_discrete_set(value, [3, 4, 5]))
     self.write(resolution_string)
Beispiel #17
0
 def power_state(self, power):
     log.info('{0}: Set LED power states to {1}'.format(
         self.name, power))
     self._set_LED_parameters(
         'setLED_HeadPowerStates', power, c_bool,
         validator=lambda v: strict_discrete_set(v, (True, False, 0, 1)))
Beispiel #18
0
 def __init__(self, dll, channel):
     super().__init__(dll)
     channel = strict_discrete_set(channel, [0, 1])
     self.channel = channel
Beispiel #19
0
 def __init__(self, dll, meas_type):
     super().__init__(dll)
     meas_type = strict_discrete_set(
         meas_type.title(), ('Energy', 'Current', 'Voltage', 'Power'))
     self.meas_type = meas_type
Beispiel #20
0
 def frame_format(self, form):
     allowed_formats = ["mono_8", "mono_16"]
     strict_discrete_set(form, allowed_formats)
     self._frame_format = form
Beispiel #21
0
 def data_format(self, setting):
     setting = strict_discrete_set(
         setting, ["ASCii", "ASC,0", "REAL,32", "INT,8", "INT,16"])
     self.write("format:data %s" % setting)
     self.check_errors
Beispiel #22
0
 def acquire_multi_waveform(self, setting):
     setting = strict_discrete_set(int(setting), (0, 1))
     self.write("acquire:muwaveform %d" % setting)
     # add/remove additional waveform properties of channels
     for ch in self.channels:
         ch._acquire_multi_waveform = bool(setting)
Beispiel #23
0
 def __init__(self, dll, sensor_type):
     super().__init__(dll)
     sensor_type = strict_discrete_set(
         sensor_type.title(), ('Photodiode', 'Thermopile', 'Pyrosensor'))
     self.sensor = sensor_type
Beispiel #24
0
 def range(self, value):
     cur_mode = self.mode
     value = strict_discrete_set(value, self.RANGES[cur_mode])
     set_range = self.RANGES[cur_mode][value]
     self.write(set_range)
Beispiel #25
0
 def auto_zero_enabled(self, value):
     az_set = int(value)
     AZ_str = "Z" + str(int(strict_discrete_set(az_set, [0, 1])))
     self.write(AZ_str)
Beispiel #26
0
 def mode(self, value):
     mode_set = self.MODES[strict_discrete_set(value, self.MODES)]
     self.write(mode_set)
Beispiel #27
0
def test_strict_discrete_set():
    assert strict_discrete_set(5, range(10)) == 5
    with pytest.raises(ValueError):
        strict_discrete_set(5.1, range(10))
    with pytest.raises(ValueError):
        strict_discrete_set(20, range(10))
Beispiel #28
0
def test_strict_discrete_set():
    assert strict_discrete_set(5, range(10)) == 5
    with pytest.raises(ValueError) as e_info:
        strict_discrete_set(5.1, range(10))
    with pytest.raises(ValueError) as e_info:
        strict_discrete_set(20, range(10))