Example #1
0
 def test_serialize_deserialize_pulse(self):
     with warnings.catch_warnings():
         warnings.filterwarnings("ignore",
                                 category=UserWarning,
                                 message="qupulse")
         period = 1e-6
         amplitude = 1.5
         sawtooth = Sequencer.make_sawtooth_wave(amplitude, period)
         serialized_pulse = sawtooth['wave']
         serialized_data = Sequencer.serialize(sawtooth)
         self.assertTrue(
             "qupulse.pulses.sequence_pulse_template.SequencePulseTemplate"
             in serialized_data)
         self.assertTrue(
             "qupulse.pulses.mapping_pulse_template.MappingPulseTemplate" in
             serialized_data)
         self.assertTrue("\"amplitude\": 0.75" in serialized_data)
         self.assertTrue("\"period\": 1000.0" in serialized_data)
         self.assertTrue("\"width\": 0.95" in serialized_data)
         self.assertTrue(
             "qupulse.pulses.table_pulse_template.TablePulseTemplate" in
             serialized_data)
         self.assertTrue("sawtooth" in serialized_data)
         deserialized_pulse = Sequencer.deserialize(serialized_data)
         self.assertEqual(
             serialized_pulse.subtemplates[0].parameter_mapping['period'],
             deserialized_pulse.subtemplates[0].parameter_mapping['period'])
         self.assertEqual(
             serialized_pulse.subtemplates[0].
             parameter_mapping['amplitude'], deserialized_pulse.
             subtemplates[0].parameter_mapping['amplitude'])
         self.assertEqual(
             serialized_pulse.subtemplates[0].parameter_mapping['width'],
             deserialized_pulse.subtemplates[0].parameter_mapping['width'])
Example #2
0
 def __make_markers(self, period):
     digitizer_marker = Sequencer.make_marker(
         period, self.digitizer_marker_uptime(),
         self.digitizer_marker_delay())
     awg_marker = Sequencer.make_marker(period, self.awg_marker_uptime(),
                                        self.awg_marker_delay())
     return dict({'m4i_mk': digitizer_marker, 'awg_mk': awg_marker})
 def test_make_pulse_table(self):
     with warnings.catch_warnings():
         warnings.filterwarnings("ignore", category=UserWarning, message="qupulse")
         amplitudes = [1, 2, 3]
         waiting_times = [1e-4, 2e-5, 3e-3]
         sampling_rate = 1e9
         pulse_data = Sequencer.make_pulse_table(amplitudes, waiting_times)
         raw_data = Sequencer.get_data(pulse_data, sampling_rate)
         self.assertTrue(raw_data[0] == amplitudes[0])
         self.assertTrue(raw_data[-1] == amplitudes[-1])
Example #4
0
    def sweep_gates_2d(self, gates, sweep_ranges, period, resolution, width=0.9375, do_upload=True):
        """ Supplies sawtooth signals to a linear combination of gates, which effectively does a 2D scan.

        Arguments:
            gates (list[dict]): A list containing two dictionaries with both the the gate name keys
                                and relative amplitude values.
            sweep_ranges (list): A list two overall amplitude of the sawtooth waves in millivolt in
                                 the x- and y-direction.
            period (float): The total period of the sawtooth signals in seconds.
            resolution (list): Two integer values with the number of sawtooth signal (pixels) in the
                               x- and y-direction.
            width (float): Width of the rising sawtooth ramp as a proportion of the total cycle.
                           Needs a value between 0 and 1. The value 1 producing a rising ramp,
                           while 0 produces a falling ramp.
            do_upload (bool, Optional): Does not upload the waves to the AWG's when set to False.

        Returns:
            A dictionary with the properties of the sawtooth signals; the original sawtooth sequence,
            the sweep ranges, the marker properties and period of the sawtooth signals.

        Example:
            >> sec_period = 1e-6
            >> resolution = [10, 10]
            >> mV_sweep_ranges = [100, 100]
            >> gates = [{'P4': 1}, {'P7': 0.1}]
            >> sweep_data = virtual_awg.sweep_gates_2d(gates, mV_sweep_ranges, period, resolution)
        """
        sequences = {}
        base_period = period / np.prod(resolution)
        sequences.update(self.make_markers(period, repetitions=1))

        period_x = resolution[0] * base_period
        for gate_name_x, rel_amplitude_x in gates[0].items():
            amplitude_x = rel_amplitude_x * sweep_ranges[0]
            sweep_wave_x = Sequencer.make_sawtooth_wave(amplitude_x, period_x, width, resolution[1])
            sequences.setdefault(gate_name_x, []).append(sweep_wave_x)

        period_y = resolution[0] * resolution[1] * base_period
        for gate_name_y, rel_amplitude_y in gates[1].items():
            amplitude_y = rel_amplitude_y * sweep_ranges[1]
            sweep_wave_y = Sequencer.make_sawtooth_wave(amplitude_y, period_y, width)
            sequences.setdefault(gate_name_y, []).append(sweep_wave_y)

        sweep_data = self.sequence_gates(sequences, do_upload)
        sweep_data.update({'sweeprange_horz': sweep_ranges[0],
                           'sweeprange_vert': sweep_ranges[1],
                           'width_horz': width,
                           'width_vert': width,
                           'resolution': resolution,
                           'start_zero': True,
                           'period': period_y, 'period_horz': period_x,
                           'samplerate': self.awgs[0].retrieve_sampling_rate(),
                           'markerdelay': self.digitizer_marker_delay()})
        return sweep_data
 def test_qupulse_sawtooth_HasCorrectProperties(self):
     with warnings.catch_warnings():
         warnings.filterwarnings("ignore", category=UserWarning, message="qupulse")
         epsilon = 1e-14
         period = 1e-3
         amplitude = 1.5
         sampling_rate = 1e9
         sequence = Sequencer.make_sawtooth_wave(amplitude, period)
         raw_data = Sequencer.get_data(sequence, sampling_rate)
         self.assertTrue(len(raw_data) == sampling_rate * period + 1)
         self.assertTrue(np.abs(np.min(raw_data) + amplitude / 2) <= epsilon)
         self.assertTrue(np.abs(np.max(raw_data) - amplitude / 2) <= epsilon)
Example #6
0
    def test_offset_raises_errors(self):
        period = 10e-9
        offset = -11e-9
        uptime = 2e-9
        with self.assertRaises(ValueError) as error:
            Sequencer.make_marker(period=period, offset=offset, uptime=uptime)
        self.assertIn('Invalid argument value for offset: |-1.1e-08| > 1e-08!', error.exception.args)

        offset = -offset
        with self.assertRaises(ValueError) as error:
            Sequencer.make_marker(period=period, offset=offset, uptime=uptime)
        self.assertIn('Invalid argument value for offset: |1.1e-08| > 1e-08!', error.exception.args)
Example #7
0
    def pulse_gates_2d(self, gates, sweep_ranges, period, resolution, do_upload=True):
        """ Supplies square signals to a linear combination of gates, which effectively does a 2D scan.

        Arguments:
            gates (list[dict]): A list containing two dictionaries with both the the gate name keys
                                and relative amplitude values.
            sweep_ranges (list): A list two overall amplitude of the square signal in millivolt in
                                 the x- and y-direction.
            period (float): The period of the square signals in seconds.
            resolution (list): Two integer values with the number of square signal (pixels) in the
                               x- and y-direction.
            do_upload (bool, Optional): Does not upload the waves to the AWG's when set to False.

        Returns:
            A dictionary with the properties of the square signals; the original square sequence,
            the sweep ranges, the marker properties and period of the square signals.

        Example:
            >> sec_period = 1e-6
            >> resolution = [10, 10]
            >> mV_sweep_ranges = [100, 100]
            >> gates = [{'P4': 1}, {'P7': 0.1}]
            >> sweep_data = virtual_awg.pulse_gates_2d(gates, mV_sweep_ranges, period, resolution)
        """
        sequences = {}
        sequences.update(self.make_markers(period))

        period_x = resolution[0] * period
        for gate_name_x, rel_amplitude_x in gates[0].items():
            amplitude_x = rel_amplitude_x * sweep_ranges[0]
            pulse_wave_x = Sequencer.make_square_wave(amplitude_x, period_x, resolution[1])
            sequences.setdefault(gate_name_x, []).append(pulse_wave_x)

        period_y = resolution[0] * resolution[1] * period
        for gate_name_y, rel_amplitude_y in gates[1].items():
            amplitude_y = rel_amplitude_y * sweep_ranges[1]
            pulse_wave_y = Sequencer.make_square_wave(amplitude_y, period_y)
            sequences.setdefault(gate_name_y, []).append(pulse_wave_y)

        sweep_data = self.sequence_gates(sequences, do_upload)
        sweep_data.update({'sweeprange_horz': sweep_ranges[0],
                           'sweeprange_vert': sweep_ranges[1],
                           'resolution': resolution,
                           'period': period_x,
                           'period_vert': period_y,
                           'samplerate': self.awgs[0].retrieve_setting('channel_sampling_rate'),
                           'markerdelay': self.awg_marker_delay()})
        return sweep_data
Example #8
0
    def pulse_gates(self,
                    gate_voltages,
                    waiting_times,
                    repetitions=1,
                    do_upload=True):
        """ Supplies custom sequences to the gates. The supplied list of voltage setpoints with
            waiting times are converted into sequences for each gate and upload to the AWG.

        Arguments:
            gate_voltages (dict): Each gate name key contains a an array with millivolt
                                  setpoint level to be converted into a sequence.
            waiting_times (list[float]): The duration in seconds of each pulse in the sequence.
            repetitions (int): The number of times to repeat the sequence.
            do_upload (bool, Optional): Does not upload the waves to the AWG's when set to False.

        Returns:
            A dictionary with the properties of the pulse waves; the original pulse sequence,
            the sweep ranges and the marker properties and period of the pulse waves.

        Example:
            >>> gates_voltages = {'P4': [50, 0, -50], 'P7': [-25, 0, 25]}
            >>> waiting_times = [1e-4, 1e-4, 1e-4]
            >>> pulse_data = virtualawg.pulse_gates(gate_voltages, waiting_times)
        """
        sequences = dict()
        period = sum(waiting_times)
        sequences.update(self.make_markers(period, repetitions))
        for gate_name, amplitudes in gate_voltages.items():
            sequences[gate_name] = Sequencer.make_pulse_table(
                amplitudes, waiting_times, repetitions, gate_name)
        sweep_data = self.sequence_gates(sequences, do_upload)
        sweep_data.update({'period': period, 'start_zero': True, 'width': 1.0})
        if VirtualAwg.__digitizer_name in self._settings.awg_map:
            sweep_data.update({'markerdelay': self.digitizer_marker_delay()})
        return sweep_data
Example #9
0
    def sequence_gates(self, gate_comb, do_upload=True):
        if do_upload:
            [awg.delete_waveforms() for awg in self.awgs]
        for number in self.__awg_range:
            sequence_channels = list()
            sequence_names = list()
            sequence_items = list()
            vpp_amplitude = self.awgs[number].retrieve_gain()
            sampling_rate = self.awgs[number].retrieve_sampling_rate()
            for gate_name, sequence in gate_comb.items():
                (awg_number, channel_number,
                 *marker_number) = self._gate_map[gate_name]
                if awg_number != number:
                    continue
                awg_to_plunger = self.__hardware.parameters['awg_to_{}'.format(
                    gate_name)].get()
                scaling_ratio = 2 * VirtualAwg.__volt_to_millivolt / awg_to_plunger / vpp_amplitude

                sample_data = Sequencer.get_data(sequence, sampling_rate)
                sequence_data = sample_data if marker_number else sample_data * scaling_ratio

                sequence_names.append('{}_{}'.format(gate_name,
                                                     sequence['name']))
                sequence_channels.append((channel_number, *marker_number))
                sequence_items.append(sequence_data)
            if do_upload:
                self.awgs[number].upload_waveforms(sequence_names,
                                                   sequence_channels,
                                                   sequence_items)
        return {'gate_comb': gate_comb}
Example #10
0
    def sweep_gates(self,
                    gates,
                    sweep_range,
                    period,
                    width=0.95,
                    do_upload=True):
        """ Sweep a set of gates with a sawtooth waveform.

        Example:
            >>> sweep_data = virtualawg.sweep_gates({'P4': 1, 'P7': 0.1}, 100, 1e-3)
        """
        sequences = dict()
        sequences.update(self.__make_markers(period))
        for gate_name, rel_amplitude in gates.items():
            amplitude = rel_amplitude * sweep_range
            sequences[gate_name] = Sequencer.make_sawtooth_wave(
                amplitude, period, width)
        sweep_data = self.sequence_gates(sequences, do_upload)
        sweep_data.update({
            'sweeprange': sweep_range,
            'period': period,
            'width': width,
            'markerdelay': self.digitizer_marker_delay()
        })
        return sweep_data
Example #11
0
    def test_qupulse_template_to_array_new_style_vs_values_old_style(self):
        with warnings.catch_warnings():
            warnings.filterwarnings("ignore",
                                    category=UserWarning,
                                    message="qupulse")
            warnings.filterwarnings(
                "ignore",
                category=DeprecationWarning,
                message="InstructionBlock API is deprecated")
            period = 1e-3
            amplitude = 1.5
            sampling_rate = 1e9
            sequence = Sequencer.make_sawtooth_wave(amplitude, period)
            template = sequence['wave']
            channels = template.defined_channels
            loop = template.create_program(
                parameters=dict(),
                measurement_mapping={w: w
                                     for w in template.measurement_names},
                channel_mapping={ch: ch
                                 for ch in channels},
                global_transformation=None,
                to_single_waveform=set())
            (_, voltages_new, _) = render(loop, sampling_rate / 1e9)

            # the value to compare to are calculated using qupulse 0.4 Sequencer class
            self.assertTrue(1000001 == len(voltages_new['sawtooth']))
            self.assertAlmostEqual(-amplitude / 2,
                                   np.min(voltages_new['sawtooth']), 12)
            self.assertAlmostEqual(amplitude / 2,
                                   np.max(voltages_new['sawtooth']), 12)
Example #12
0
    def sequence_gates(self, sequences, do_upload=True):
        """ The base function for uploading sequences to the AWG's. The sequences must be
            constructed using the qtt.instrument_drivers.virtualAwg.sequencer.Sequencer class.

        Arguments:
            sequences (dict): A dictionary with names as keys and sequences as values.
            do_upload (bool, Optional): Does not upload the waves to the AWG's when set to False.

        Example:
            >> from qtt.instrument_drivers.virtualAwg.sequencer import Sequencer.
            >> amplitude = 1.5
            >> period_in_seconds = 1e-6
            >> sawtooth_signal = Sequencer.make_sawtooth_wave(amplitude, period_in_seconds)
            >> virtual_awg.sequence_gates(sawtooth_signal)
        """
        upload_data = []
        settings_data = {}

        if do_upload:
            _ = [awg.delete_waveforms() for awg in self.awgs]

        for number in self.__awg_range:
            sequence_channels = []
            sequence_names = []
            sequence_items = []

            gain_factor = self.awgs[number].retrieve_gain()
            vpp_amplitude = 2 * gain_factor
            sampling_rate = self.awgs[number].retrieve_sampling_rate()
            settings_data[number] = {'vpp_amplitude': vpp_amplitude, 'sampling_rate': sampling_rate}

            for gate_name, sequence in sequences.items():
                (awg_number, channel_number, *marker_number) = self._settings.awg_map[gate_name]
                if awg_number != number:
                    continue

                waveforms = [Sequencer.get_data(waveform, sampling_rate) for waveform in sequence]
                sequence_data = np.sum(waveforms, 0)
                sequence_data = sequence_data[:-1]
                if not marker_number:
                    awg_to_gate = self._settings.parameters[f'awg_to_{gate_name}'].get()
                    scaling_ratio = 1 / (awg_to_gate * gain_factor)
                    settings_data[number][gate_name] = {'scaling_ratio': scaling_ratio}
                    sequence_data *= scaling_ratio

                sequence_name = sequence[0]['name']
                sequence_names.append(f'{gate_name}_{sequence_name}')
                sequence_channels.append((channel_number, *marker_number))
                sequence_items.append(sequence_data)

            upload_data.append((sequence_names, sequence_channels, sequence_items))
            if do_upload and sequence_items:
                self.awgs[number].upload_waveforms(sequence_names, sequence_channels, sequence_items)

        sequence_data = {'gate_comb': sequences, 'upload_data': upload_data, 'settings': settings_data}
        if self.enable_debug:
            self._latest_sequence_data = sequence_data
        return sequence_data
Example #13
0
    def sweep_gates_2d(self,
                       gates,
                       sweep_ranges,
                       period,
                       resolution,
                       width=0.95,
                       do_upload=True):
        sequences = dict()
        sequences.update(self.__make_markers(period))

        period_x = resolution[0] * period
        for gate_name_x, rel_amplitude_x in gates[0].items():
            amplitude_x = rel_amplitude_x * sweep_ranges[0]
            sequences[gate_name_x] = Sequencer.make_sawtooth_wave(
                amplitude_x, period_x, width)

        period_y = resolution[0] * resolution[1] * period
        for gate_name_y, rel_amplitude_y in gates[1].items():
            amplitude_y = rel_amplitude_y * sweep_ranges[1]
            sequences[gate_name_y] = Sequencer.make_sawtooth_wave(
                amplitude_y, period_y, width)

        sweep_data = self.sequence_gates(sequences, do_upload)
        sweep_data.update({
            'sweeprange_horz':
            sweep_ranges[0],
            'sweeprange_vert':
            sweep_ranges[1],
            'width_horz':
            0.95,
            'width_vert':
            0.95,
            'resolution':
            resolution,
            'period':
            period_y,
            'period_horz':
            period_x,
            'samplerate':
            self.awgs[0].retrieve_setting('sampling_rate'),
            'markerdelay':
            self.awg_marker_delay()
        })
        return sweep_data
 def test_raw_wave_HasCorrectProperties(self):
     with warnings.catch_warnings():
         warnings.filterwarnings("ignore", category=UserWarning, message="qupulse")
         period = 1e-3
         sampling_rate = 1e9
         name = 'test_raw_data'
         sequence = {'name': name, 'wave': [0] * int(period * sampling_rate + 1),
                     'type': DataTypes.RAW_DATA}
         raw_data = Sequencer.get_data(sequence, sampling_rate)
         self.assertTrue(len(raw_data) == sampling_rate * period + 1)
         self.assertTrue(np.min(raw_data) == 0)
Example #15
0
    def test_make_marker_no_regression(self):
        period = 10e-9
        offset = 0
        uptime = 4e-9
        marker = Sequencer.make_marker(period=period, offset=offset, uptime=uptime)
        self.assertEqual(offset, marker['offset'])
        self.assertEqual(uptime, marker['uptime'])

        parameters = marker['wave'].subtemplates[0].parameter_mapping
        self.assertEqual(10, parameters['period'])
        self.assertEqual(0, parameters['offset'])
        self.assertEqual(4, parameters['uptime'])
Example #16
0
    def test_make_marker_negative_offset(self):
        period = 10e-9
        offset = -3e-9
        uptime = 2e-9
        marker = Sequencer.make_marker(period=period, offset=offset, uptime=uptime)
        self.assertEqual(offset, marker['offset'])
        self.assertEqual(uptime, marker['uptime'])

        parameters = marker['wave'].subtemplates[0].parameter_mapping
        self.assertEqual(7, parameters['offset'])
        self.assertEqual(2, parameters['uptime'])
        self.assertEqual(10, parameters['period'])
Example #17
0
    def make_markers(self, period, repetitions=1):
        """ Constructs the markers for triggering the digitizer readout and the slave AWG
            start sequence. The sequence length equals the period x repetitions.

        Arguments:
            period (float): The period of the markers in seconds.
            repetitions (int): The number of markers in the sequence.
        """
        marker_properties = {}
        uptime = self.digitizer_marker_uptime()
        delay = self.digitizer_marker_delay()

        if VirtualAwg.__digitizer_name in self._settings.awg_map:
            digitizer_marker = Sequencer.make_marker(period, uptime, delay, repetitions)
            marker_properties[VirtualAwg.__digitizer_name] = [digitizer_marker]

        if VirtualAwg.__awg_slave_name in self._settings.awg_map:
            awg_marker = Sequencer.make_marker(period, uptime, delay, repetitions)
            marker_properties[VirtualAwg.__awg_slave_name] = [awg_marker]

        return marker_properties
Example #18
0
 def pulse_gates(self, gates, sweep_range, period, do_upload=True):
     sequences = dict()
     sequences.update(self.__make_markers(period))
     for gate_name, rel_amplitude in gates.items():
         amplitude = rel_amplitude * sweep_range
         sequences[gate_name] = Sequencer.make_square_wave(
             amplitude, period)
     sweep_data = self.sequence_gates(sequences, do_upload)
     return sweep_data.update({
         'sweeprange': sweep_range,
         'period': period,
         'markerdelay': self.digitizer_marker_delay()
     })
Example #19
0
    def test_make_marker_negative_offset_rollover(self):
        period = 10e-9
        offset = -2e-9
        uptime = 4e-9
        with patch('warnings.warn') as warn:
            marker = Sequencer.make_marker(period=period, offset=offset, uptime=uptime)
            warn.assert_called_once_with('Marker rolls over to subsequent period.')
        self.assertEqual(offset, marker['offset'])
        self.assertEqual(uptime, marker['uptime'])

        parameters = marker['wave'].subtemplates[0].parameter_mapping
        self.assertEqual(8, parameters['offset'])
        self.assertEqual(4, parameters['uptime'])
        self.assertEqual(10, parameters['period'])
Example #20
0
    def sweep_gates(self,
                    gates,
                    sweep_range,
                    period,
                    width=0.9375,
                    do_upload=True):
        """ Supplies a sawtooth wave to the given gates and returns the settings required
            for processing and constructing the readout times for the digitizer.

        Arguments:
            gates (dict): Contains the gate name keys with relative amplitude values.
            sweep_range (float): The peak-to-peak amplitude of the sawtooth waves in millivolt.
            period (float): The period of the pulse waves in seconds.
            width (float): Width of the rising sawtooth ramp as a proportion of the total cycle.
                           Needs a value between 0 and 1. The value 1 producing a rising ramp,
                           while 0 produces a falling ramp.
            do_upload (bool, Optional): Does not upload the waves to the AWG's when set to False.

        Returns:
            A dictionary with the properties of the pulse waves; the original sawtooth sequence,
            the sweep ranges and the marker properties and period of the sawtooth waves.

        Example:
            >> sec_period = 1e-6
            >> mV_sweep_range = 100
            >> gates = {'P4': 1, 'P7': 0.1}
            >> sweep_data = virtual_awg.sweep_gates(gates, 100, 1e-3)
        """
        sequences = dict()
        sequences.update(self.make_markers(period))

        for gate_name, rel_amplitude in gates.items():
            amplitude = rel_amplitude * sweep_range
            sweep_wave = Sequencer.make_sawtooth_wave(amplitude, period, width)
            sequences.setdefault(gate_name, []).append(sweep_wave)

        sweep_data = self.sequence_gates(sequences, do_upload)
        sweep_data.update({
            'sweeprange': sweep_range,
            'period': period,
            'width': width,
            'start_zero': True,
            '_gates': gates
        })

        if VirtualAwg.__digitizer_name in self._settings.awg_map:
            sweep_data.update({'markerdelay': self.digitizer_marker_delay()})

        return sweep_data
Example #21
0
    def test_qupulse_template_to_array_new_style_vs_old_style(self):
        with warnings.catch_warnings():
            warnings.filterwarnings("ignore",
                                    category=UserWarning,
                                    message="qupulse")
            warnings.filterwarnings(
                "ignore",
                category=DeprecationWarning,
                message="InstructionBlock API is deprecated")
            period = 1e-3
            amplitude = 1.5
            sampling_rate = 1e9
            sequence = Sequencer.make_sawtooth_wave(amplitude, period)
            template = sequence['wave']
            channels = template.defined_channels
            sequencer = Sequencing()
            sequencer.push(
                template,
                dict(),
                channel_mapping={ch: ch
                                 for ch in channels},
                window_mapping={w: w
                                for w in template.measurement_names})
            instructions = sequencer.build()
            if not sequencer.has_finished():
                raise PlottingNotPossibleException(template)
            (_, voltages_old, _) = render(instructions, sampling_rate / 1e9)

            loop = template.create_program(
                parameters=dict(),
                measurement_mapping={w: w
                                     for w in template.measurement_names},
                channel_mapping={ch: ch
                                 for ch in channels},
                global_transformation=None,
                to_single_waveform=set())

            (_, voltages_new, _) = render(loop, sampling_rate / 1e9)
            self.assertTrue(len(voltages_old) == len(voltages_new))
            self.assertTrue(np.min(voltages_old) == np.min(voltages_old))
            self.assertTrue(np.max(voltages_old) == np.max(voltages_old))
Example #22
0
    def test_uptime_raises_errors(self):
        period = 10e-9
        offset = 0
        uptime = 0
        with self.assertRaises(ValueError) as error:
            Sequencer.make_marker(period=period, offset=offset, uptime=uptime)
        self.assertIn("Invalid argument value for uptime '0'!", error.exception.args)

        uptime = 11e-9
        with self.assertRaises(ValueError) as error:
            Sequencer.make_marker(period=period, offset=offset, uptime=uptime)
        self.assertIn("Invalid argument value for uptime '1.1e-08'!", error.exception.args)

        uptime = -1e-9
        with self.assertRaises(ValueError) as error:
            Sequencer.make_marker(period=period, offset=offset, uptime=uptime)
        self.assertIn("Invalid argument value for uptime '-1e-09'!", error.exception.args)