def initialize_idle_program(self): """Make sure we can arm the idle program which plays the idle waveform(default 0V) on all channels.""" self.assert_synchronized() self.logger.info("Initializing idle progam") if self._idle_waveform in self._waveforms.by_data: self.logger.debug("Idle waveform found on device") idle_waveform_name = self._waveforms.by_data[self._idle_waveform].name else: self.logger.debug("Idle waveform not found on device") idle_waveform_name = self.idle_pulse_name(self._idle_waveform.size) self._upload_waveform(self._idle_waveform, idle_waveform_name) idle_sequence_element = tek_awg.SequenceEntry(entries=[idle_waveform_name] * self.device.n_channels, wait=False, loop_inf=True, loop_count=None, goto_ind=None, goto_state=False, jmp_type='OFF', jmp_ind=None) try: self._idle_program_index = self._sequence_entries.index(idle_sequence_element) + 1 except ValueError: idle_index, *_ = self._get_empty_sequence_positions(1) self.logger.debug("Idle sequence entry not found on device. Uploading it to %d", idle_index) self._upload_sequencing_element(idle_index, idle_sequence_element) self._idle_program_index = self._sequence_entries.index(idle_sequence_element) + 1 else: self.logger.debug("Idle sequence entry found on device: %d", self._idle_program_index)
def test_parse_program(self): ill_formed_program = Loop(children=[Loop(children=[Loop()])]) with self.assertRaisesRegex(AssertionError, 'Invalid program depth'): parse_program(ill_formed_program, (), (), TimeType(), (), (), ()) channels = ('A', 'B', None, None) markers = (('A1', None), (None, None), (None, 'C2'), (None, None)) # we do test offset handling separately amplitudes = (1, 1, 1, 1) offsets = (0, 0, 0, 0) voltage_transformations = tuple( mock.Mock(wraps=lambda x: x) for _ in range(4)) used_channels = {'A', 'B', 'A1', 'C2'} wf_defined_channels = used_channels & {'other'} sampled_6 = [ np.zeros((6, )), np.arange(6) / 6, np.ones((6, )) * 0.42, np.array([0., .1, .2, 0., 1, 0]), np.array([1., .0, .0, 0., 0, 1]) ] sampled_4 = [ np.zeros((4, )), np.arange(-4, 0) / 4, np.ones((4, )) * 0.2, np.array([0., -.1, -.2, 0.]), np.array([0., 0, 0, 1.]) ] sample_rate_in_GHz = TimeType.from_fraction(1, 2) # channel A is the same in wfs_6[1] and wfs_6[2] wfs_6 = [ DummyWaveform(duration=12, sample_output={ 'A': sampled_6[0], 'B': sampled_6[0], 'A1': sampled_6[0], 'C2': sampled_6[0] }, defined_channels=used_channels), DummyWaveform(duration=12, sample_output={ 'A': sampled_6[1], 'B': sampled_6[2], 'A1': sampled_6[3], 'C2': sampled_6[4] }, defined_channels=used_channels), DummyWaveform(duration=12, sample_output={ 'A': sampled_6[1], 'B': sampled_6[0], 'A1': sampled_6[3], 'C2': sampled_6[2] }, defined_channels=used_channels) ] wfs_4 = [ DummyWaveform(duration=8, sample_output={ 'A': sampled_4[0], 'B': sampled_4[0], 'A1': sampled_4[2], 'C2': sampled_4[3] }, defined_channels=used_channels), DummyWaveform(duration=8, sample_output={ 'A': sampled_4[1], 'B': sampled_4[2], 'A1': sampled_4[2], 'C2': sampled_4[3] }, defined_channels=used_channels), DummyWaveform(duration=8, sample_output={ 'A': sampled_4[2], 'B': sampled_4[0], 'A1': sampled_4[2], 'C2': sampled_4[3] }, defined_channels=used_channels) ] # unset is equal to sampled_n[0] binary_waveforms_6 = [(0, 0, 0), (0, 0, 0), (0, 0, 0), (1, 3, 0), (2, 0, 0), (0, 0, 4), (1, 3, 0), (0, 0, 0), (0, 0, 2)] binary_waveforms_4 = [(0, 2, 0), (0, 0, 0), (0, 0, 3), (1, 2, 0), (2, 0, 0), (0, 0, 3), (2, 2, 0), (0, 0, 0), (0, 0, 3)] n_bin_waveforms = len(set(binary_waveforms_6)) + len( set(binary_waveforms_4)) tek_waveforms_6 = [ tek_awg.Waveform(channel=voltage_to_uint16(sampled_6[ch], 1, 0, 14), marker_1=sampled_6[m1], marker_2=sampled_6[m2]) for (ch, m1, m2) in binary_waveforms_6 ] tek_waveforms_4 = [ tek_awg.Waveform(channel=voltage_to_uint16(sampled_4[ch], 1, 0, 14), marker_1=sampled_4[m1], marker_2=sampled_4[m2]) for (ch, m1, m2) in binary_waveforms_4 ] tek_waveforms = set(tek_waveforms_4 + tek_waveforms_6) # equivalent of wfs_6 tek_6 = [ tek_waveforms_6[:3] + [6], tek_waveforms_6[3:6] + [6], tek_waveforms_6[6:] + [6] ] tek_4 = [ tek_waveforms_4[:3] + [4], tek_waveforms_4[3:6] + [4], tek_waveforms_4[6:] + [4] ] program = [(wfs_6[0], 1), (wfs_4[0], 2), (wfs_6[0], 3), (wfs_6[1], 4), (wfs_4[1], 5), (wfs_6[2], 6), (wfs_4[2], 7), (wfs_6[1], 8), (wfs_6[2], 9)] expected_sequence_entries_wfs = [ tek_6[0], tek_4[0], tek_6[0], tek_6[1], tek_4[1], tek_6[2], tek_4[2], tek_6[1], tek_6[2] ] expected_sequence_entries = tuple( tek_awg.SequenceEntry(entries=wfs, loop_count=idx + 1) for idx, wfs in enumerate(expected_sequence_entries_wfs)) loop_program = Loop(children=[ Loop(waveform=waveform, repetition_count=repetition_count) for waveform, repetition_count in program ]) sequence_entries, waveforms = parse_program( program=loop_program, channels=channels, markers=markers, sample_rate=sample_rate_in_GHz * 10**9, amplitudes=amplitudes, voltage_transformations=voltage_transformations, offsets=offsets) waveform_set = set(waveforms) self.assertEqual(len(waveform_set), len(waveforms)) self.assertIn(4, waveforms) self.assertIn(6, waveforms) waveform_set = waveform_set - {4, 6} self.assertEqual(len(waveform_set), n_bin_waveforms) self.assertEqual(tek_waveforms, waveform_set) self.assertEqual(len(sequence_entries), 9) self.assertEqual(expected_sequence_entries, sequence_entries)
def _process_program(self, name: str, tek_program: TektronixProgram) -> Tuple[Sequence[tek_awg.SequenceEntry], Mapping[tek_awg.Waveform, str]]: """Detect which waveforms are missing and create sequencing entries. This function does not communicate with the device. Args: name: tek_program: Returns: sequencing_elements: List of SequenceEntries waveforms_to_upload: Missing waveforms with names """ waveforms_to_upload = dict() required_idle_pulses = dict() sequencing_elements = [] for entries, *sequencing_info in tek_program.get_sequencing_elements(): new_entries = [] for entry in entries: if isinstance(entry, str): # check that we know the waveform wf_name = self._waveforms.by_name[entry].name elif isinstance(entry, tek_awg.Waveform): if entry in self._waveforms.by_data: wf_name = self._waveforms.by_data[entry].name elif entry in waveforms_to_upload: wf_name = waveforms_to_upload[entry] else: wf_name = name + '_' + str(abs(hash(entry))) waveforms_to_upload[entry] = wf_name else: assert entry - int(entry) == 0 entry = int(entry) if entry in required_idle_pulses: wf_name = required_idle_pulses[entry] else: wf_name = self.idle_pulse_name(entry) wf_data = self.make_idle_waveform(entry) if wf_data in self._waveforms.by_data: wf_name = self._waveforms.by_data[wf_data].name else: # rename waveform to idle waveform for clarity waveforms_to_upload[wf_data] = wf_name required_idle_pulses[entry] = wf_name new_entries.append(wf_name) sequencing_elements.append(tek_awg.SequenceEntry(new_entries, *sequencing_info)) return sequencing_elements, waveforms_to_upload
def parse_program(program: Loop, channels: Tuple[Optional[ChannelID], ...], markers: Tuple[Tuple[Optional[ChannelID], Optional[ChannelID]], ...], sample_rate: TimeType, amplitudes: Tuple[float, ...], voltage_transformations: Tuple[Callable, ...], offsets: Tuple[float, ...] = None) -> Tuple[Sequence[tek_awg.SequenceEntry], Sequence[tek_awg.Waveform]]: """Convert the program into a sequence of sequence table entries and a sequence of waveforms that can be uploaded to the device.""" assert program.depth() == 1, ("Invalid program depth: %d" % program.depth()) assert program.repetition_count == 1, ("Cannot repeat program a finite number of times (only once not %d)" % program.repetition_count) # For backward compatibility # EDIT: I think this is not needed? (Simon) if offsets is None: offsets = (0.,) * len(amplitudes) assert len(channels) == len(markers) == len(amplitudes) == len(voltage_transformations) == len(offsets) sequencing_elements = [] ch_waveforms = {} bin_waveforms = {} sample_rate_in_GHz = sample_rate / 10**9 time_array, n_samples = get_sample_times([loop.waveform for loop in program], sample_rate_in_GHz=sample_rate_in_GHz) channel_wise_kwargs = [dict(voltage_to_uint16_kwargs=dict(output_amplitude=amplitude, output_offset=offset, resolution=14), voltage_transformation=voltage_trafo) for amplitude, offset, voltage_trafo in zip(amplitudes, offsets, voltage_transformations)] # List of Tuple[positional channel tuple, set chs to sample] channel_infos = [((channel, marker_1, marker_2), {channel, marker_1, marker_2} - {None}) for channel, (marker_1, marker_2) in zip(channels, markers)] for n_sample, loop in zip(n_samples, program): entries = [] for (positional_chs, chs_to_sample), kwargs in zip(channel_infos, channel_wise_kwargs): if not chs_to_sample: entries.append(n_sample) bin_waveforms[n_sample] = None else: ch_waveform = loop.waveform.get_subset_for_channels(chs_to_sample) if ch_waveform not in ch_waveforms: bin_waveform = _make_binary_waveform(ch_waveform, time_array[:n_sample], *positional_chs, **kwargs) if bin_waveform in bin_waveforms: # use identical binary waveform already created to save memory bin_waveform = ch_waveforms[bin_waveforms[bin_waveform]] else: bin_waveforms[bin_waveform] = ch_waveform ch_waveforms[ch_waveform] = bin_waveform entries.append(ch_waveforms[ch_waveform]) sequencing_elements.append( tek_awg.SequenceEntry(entries=entries, loop_count=loop.repetition_count) ) return tuple(sequencing_elements), tuple(bin_waveforms.keys())