コード例 #1
0
    def _render(self, sample_rate, ref_channel_states):
        '''
        make a full rendering of the waveform at a predermined sample rate.
        '''
        # express in Gs/s
        sample_rate = sample_rate * 1e-9

        t_tot = self.total_time

        # get number of points that need to be rendered
        t_tot_pt = iround(t_tot * sample_rate) + 1

        my_sequence = np.zeros(t_tot_pt)

        for data_points in self.my_marker_data:
            start = iround(data_points.start * sample_rate)
            stop = iround(data_points.stop * sample_rate)

            my_sequence[start:stop] = 1 * self.pulse_amplitude

        return my_sequence[:-1]
コード例 #2
0
    def _generate_digitizer_sequences(self, job):

        for name, value in job.schedule_params.items():
            if name.startswith('dig_trigger_') or name.startswith('dig_wait'):
                raise Exception('HVI triggers not supported with QS')

        pxi_triggers = {}
        for seg in job.sequence:
            if isinstance(seg, conditional_segment):
                acq_names = get_acquisition_names(seg)
                pxi = 6
                for acq in acq_names:
                    pxi_triggers[acq] = pxi
                    pxi += 1

        logging.debug(f'PXI triggers: {pxi_triggers}')

        offset = int(self.max_pre_start_ns)
        segments = self.segments
        for channel_name, channel in self.digitizer_channels.items():
            t_start = -offset
            sequence = AcquisitionSequenceBuilder(channel_name, t_start)
            job.digitizer_sequences[channel_name] = sequence

            for iseg, (seg, seg_render) in enumerate(zip(job.sequence, segments)):
                if isinstance(seg, conditional_segment):
                    logging.debug(f'conditional for {channel_name}')
                    # TODO @@@@ lookup acquisitions and set pxi trigger.
                    seg_ch = get_conditional_channel(seg, channel_name)
                else:
                    seg_ch = seg[channel_name]
                acquisition_data = seg_ch._get_data_all_at(job.index).get_data()
                for acquisition in acquisition_data:
                    t_acq = seg_render.t_start + acquisition.start

                    if channel.downsample_rate is not None:
                        period_ns = iround(1e8/channel.downsample_rate) * 10
                        n_cycles = int(acquisition.t_measure / period_ns)
                        t_integrate = period_ns
                    else:
                        t_integrate = acquisition.t_measure
                        n_cycles = 1
                    pxi_trigger = pxi_triggers.get(str(acquisition.ref), None)
                    sequence.acquire(t_acq, t_integrate, n_cycles,
                                     threshold=acquisition.threshold,
                                     pxi_trigger=pxi_trigger)

                    logging.debug(f'Acq: {acquisition.ref}: {pxi_trigger}')
            sequence.close()
コード例 #3
0
    def _set_channel_raw(self, data, index):
        digitizer_channels = self._description.digitizer_channels

        # TODO @@@ works only for 1 digitzer
        # FIX @@@ this numbering assumes that the channels in the pulselib configuration is equal to
        #         the active channels of the digitizer. Unfortunately, this doesn't have to be true.
        # get digitizer parameter result numbering
        output_channels = []
        for channel in digitizer_channels.values():
            acquisitions = self._get_acquisitions(channel.name, index)
            if len(acquisitions) > 0:
                output_channels += channel.channel_numbers
        output_channels.sort()

        # set raw values
        self._channel_raw = {}
        for channel in digitizer_channels.values():
            acquisitions = self._get_acquisitions(channel.name, index)
            if len(acquisitions) == 0:
                self._channel_raw[channel.name] = np.zeros(
                    0, dtype=np.complex if channel.iq_out else np.float)
                continue
            if isinstance(channel, digitizer_channel):
                # this can be complex valued output with LO modulation or phase shift in digitizer (FPGA)
                ch = output_channels.index(channel.channel_number)
                ch_raw = data[ch]
            elif isinstance(channel, digitizer_channel_iq):
                ch_I = output_channels.index(channel.channel_numbers[0])
                ch_Q = output_channels.index(channel.channel_numbers[1])
                ch_raw = (data[ch_I] + 1j * data[ch_Q]) * np.exp(
                    1j * channel.phase)
            else:
                raise NotImplementedError(
                    f'Unknown channel type {type(channel)}')

            if not channel.iq_out:
                ch_raw = ch_raw.real

            if channel.downsample_rate is None:
                self._channel_raw[channel.name] = ch_raw.reshape(
                    (-1, len(acquisitions))).T
            else:
                period_ns = iround(1e8 / channel.downsample_rate) * 10
                n_samples = sum(
                    int(acq.t_measure / period_ns) for acq in acquisitions)
                self._channel_raw[channel.name] = ch_raw.reshape(
                    (-1, n_samples)).T
コード例 #4
0
    def render_MW_and_custom(self, sample_rate, ref_channel_states):
        '''
        Render MW pulses and custom data in 'rendered_elements'.
        '''
        elements = []

        self._pre_process()

        # express in Gs/s
        sample_rate = sample_rate * 1e-9

        # render MW pulses.
        # create list with phase shifts per ref_channel
        phase_shifts_channels = {}
        for ps in self.phase_shifts:
            ps_ch = phase_shifts_channels.setdefault(ps.channel_name, [])
            ps_ch.append(ps)

        for IQ_data_single_object in self.MW_pulse_data:
            # start stop time of MW pulse

            start_pulse = IQ_data_single_object.start
            stop_pulse = IQ_data_single_object.stop

            # max amp, freq and phase.
            amp = IQ_data_single_object.amplitude
            freq = IQ_data_single_object.frequency
            phase = IQ_data_single_object.start_phase
            if ref_channel_states and IQ_data_single_object.ref_channel in ref_channel_states.start_phase:
                ref_start_time = ref_channel_states.start_time
                ref_start_phase = ref_channel_states.start_phase[
                    IQ_data_single_object.ref_channel]
                phase_shift = 0
                if IQ_data_single_object.ref_channel in phase_shifts_channels:
                    for ps in phase_shifts_channels[
                            IQ_data_single_object.ref_channel]:
                        if ps.time <= start_pulse:
                            phase_shift += ps.phase_shift
            else:
                ref_start_time = 0
                ref_start_phase = 0
                phase_shift = 0

            # envelope data of the pulse
            if IQ_data_single_object.envelope is None:
                IQ_data_single_object.envelope = envelope_generator()

            amp_envelope = IQ_data_single_object.envelope.get_AM_envelope(
                (stop_pulse - start_pulse), sample_rate)
            phase_envelope = IQ_data_single_object.envelope.get_PM_envelope(
                (stop_pulse - start_pulse), sample_rate)

            #self.baseband_pulse_data[-1,0] convert to point numbers
            n_pt = int((stop_pulse - start_pulse) * sample_rate) if isinstance(
                amp_envelope, float) else len(amp_envelope)
            start_pt = iround(start_pulse * sample_rate)
            stop_pt = start_pt + n_pt

            # add the sin pulse
            total_phase = phase_shift + phase + phase_envelope + ref_start_phase
            t = start_pt + ref_start_time / sample_rate + np.arange(n_pt)
            wvf = amp * amp_envelope * np.sin(2 * np.pi * freq / sample_rate *
                                              1e-9 * t + total_phase)
            elements.append(rendered_element(start_pt, stop_pt, wvf))

        for custom_pulse in self.custom_pulse_data:
            wvf = self._render_custom_pulse(custom_pulse, sample_rate * 1e9)
            start_pt = iround(custom_pulse.start * sample_rate)
            stop_pt = start_pt + len(wvf)
            elements.append(rendered_element(start_pt, stop_pt, wvf))

        return self._merge_elements(elements)
コード例 #5
0
    def _render(self, sample_rate, ref_channel_states):
        '''
        make a full rendering of the waveform at a predetermined sample rate.
        '''
        self._pre_process()

        # express in Gs/s
        sample_rate = sample_rate * 1e-9

        t_tot = self.total_time

        # get number of points that need to be rendered
        t_tot_pt = iround(t_tot * sample_rate) + 1

        wvf = np.zeros([int(t_tot_pt)])

        t_pt = iround(self._times * sample_rate)

        for i in range(len(t_pt) - 1):
            pt0 = t_pt[i]
            pt1 = t_pt[i + 1]
            if pt0 != pt1:
                if self._ramps[i] != 0:
                    wvf[pt0:pt1] = np.linspace(self._amplitudes[i],
                                               self._amplitudes_end[i + 1],
                                               pt1 - pt0 + 1)[:-1]
                else:
                    wvf[pt0:pt1] = self._amplitudes[i]

        # render MW pulses.
        # create list with phase shifts per ref_channel
        phase_shifts_channels = {}
        for ps in self.phase_shifts:
            ps_ch = phase_shifts_channels.setdefault(ps.channel_name, [])
            ps_ch.append(ps)

        for IQ_data_single_object in self.MW_pulse_data:
            # start stop time of MW pulse

            start_pulse = IQ_data_single_object.start
            stop_pulse = IQ_data_single_object.stop

            # max amp, freq and phase.
            amp = IQ_data_single_object.amplitude
            freq = IQ_data_single_object.frequency
            phase = IQ_data_single_object.start_phase
            if ref_channel_states and IQ_data_single_object.ref_channel in ref_channel_states.start_phase:
                ref_start_time = ref_channel_states.start_time
                ref_start_phase = ref_channel_states.start_phase[
                    IQ_data_single_object.ref_channel]
                if IQ_data_single_object.ref_channel in phase_shifts_channels:
                    phase_shifts = [
                        ps.phase_shift for ps in phase_shifts_channels[
                            IQ_data_single_object.ref_channel]
                        if ps.time <= start_pulse
                    ]
                    phase_shift = sum(phase_shifts)
                else:
                    phase_shift = 0
            else:
                ref_start_time = 0
                ref_start_phase = 0
                phase_shift = 0

            # envelope data of the pulse
            if IQ_data_single_object.envelope is None:
                IQ_data_single_object.envelope = envelope_generator()

            amp_envelope = IQ_data_single_object.envelope.get_AM_envelope(
                (stop_pulse - start_pulse), sample_rate)
            phase_envelope = np.asarray(
                IQ_data_single_object.envelope.get_PM_envelope(
                    (stop_pulse - start_pulse), sample_rate))

            #self.baseband_pulse_data[-1,0] convert to point numbers
            n_pt = int((stop_pulse - start_pulse) * sample_rate) if isinstance(
                amp_envelope, float) else len(amp_envelope)
            start_pt = iround(start_pulse * sample_rate)
            stop_pt = start_pt + n_pt

            # add the sin pulse
            total_phase = phase_shift + phase + phase_envelope + ref_start_phase
            t = start_pt + ref_start_time / sample_rate + np.arange(n_pt)
            wvf[start_pt:stop_pt] += amp * amp_envelope * np.sin(
                2 * np.pi * freq / sample_rate * 1e-9 * t + total_phase)

        for custom_pulse in self.custom_pulse_data:
            data = self._render_custom_pulse(custom_pulse, sample_rate * 1e9)
            start_pt = iround(custom_pulse.start * sample_rate)
            stop_pt = start_pt + len(data)
            wvf[start_pt:stop_pt] += data

        # remove last value. t_tot_pt = t_tot + 1. Last value is always 0. It is only needed in the loop on the pulses.
        return wvf[:-1]
コード例 #6
0
    def _generate_upload_wvf(self, job, awg_upload_func):
        segments = self.segments
        sections = job.upload_info.sections
        ref_channel_states = RefChannels(0)

        # loop over all qubit channels to accumulate total phase shift
        for i in range(len(job.sequence)):
            ref_channel_states.start_phases_all.append(dict())
        for channel_name, qubit_channel in self.qubit_channels.items():
            if (QsUploader.use_iq_sequencers
                and channel_name in self.sequencer_channels):
                # skip IQ sequencer channels
                continue
            phase = 0
            for iseg,seg in enumerate(job.sequence):
                ref_channel_states.start_phases_all[iseg][channel_name] = phase
                seg_ch = seg[channel_name]
                phase += seg_ch.get_accumulated_phase(job.index)

        for channel_name, channel_info in self.channels.items():
            if (QsUploader.use_iq_sequencers
                and channel_name in self.sequencer_out_channels):
                # skip IQ sequencer channels
                continue
            if (QsUploader.use_baseband_sequencers
                and channel_name in self.sequencer_channels):
                # skip baseband sequencer channels
                continue

            section = sections[0]
            buffer = np.zeros(section.npt)
            bias_T_compensation_mV = 0

            for iseg,(seg,seg_render) in enumerate(zip(job.sequence,segments)):

                sample_rate = seg_render.sample_rate
                n_delay = iround(channel_info.delay_ns * sample_rate)

                if isinstance(seg, conditional_segment):
                    logging.debug(f'conditional for {channel_name}')
                    seg_ch = get_conditional_channel(seg, channel_name)
                else:
                    seg_ch = seg[channel_name]
                ref_channel_states.start_time = seg_render.t_start
                ref_channel_states.start_phase = ref_channel_states.start_phases_all[iseg]
                start = time.perf_counter()
                #print(f'start: {channel_name}.{iseg}: {ref_channel_states.start_time}')
                wvf = seg_ch.get_segment(job.index, sample_rate*1e9, ref_channel_states)
                duration = time.perf_counter() - start
                logging.debug(f'generated [{job.index}]{iseg}:{channel_name} {len(wvf)} Sa, in {duration*1000:6.3f} ms')

                if len(wvf) != seg_render.npt:
                    logging.warn(f'waveform {iseg}:{channel_name} {len(wvf)} Sa <> sequence length {seg_render.npt}')

                i_start = 0
                if seg_render.start_section:
                    if section != seg_render.start_section:
                        logging.error(f'OOPS section mismatch {iseg}, {channel_name}')

                    # add n_start_transition - n_delay to start_section
#                    n_delay_welding = iround(channel_info.delay_ns * section.sample_rate)
                    t_welding = (section.t_end - seg_render.t_start)
                    i_start = iround(t_welding*sample_rate) - n_delay
                    n_section = iround(t_welding*section.sample_rate) + iround(-channel_info.delay_ns * section.sample_rate)

                    if n_section > 0:
                        if iround(n_section*sample_rate/section.sample_rate) >= len(wvf):
                            raise Exception(f'segment {iseg} too short for welding. (nwelding:{n_section}, len_wvf:{len(wvf)})')

                        isub = [iround(i*sample_rate/section.sample_rate) for i in np.arange(n_section)]
                        welding_samples = np.take(wvf, isub)
                        buffer[-n_section:] = welding_samples

                    bias_T_compensation_mV = self._add_bias_T_compensation(buffer, bias_T_compensation_mV,
                                                                           section.sample_rate, channel_info)
                    self._upload_wvf(job, channel_name, buffer, channel_info.amplitude, channel_info.attenuation,
                                     section.sample_rate, awg_upload_func)

                    section = seg_render.section
                    buffer = np.zeros(section.npt)


                if seg_render.end_section:
                    next_section = seg_render.end_section
                    # add n_end_transition + n_delay to next section. First complete this section
                    n_delay_welding = iround(channel_info.delay_ns * section.sample_rate)
                    t_welding = (seg_render.t_end - next_section.t_start)
                    i_end = len(wvf) - iround(t_welding*sample_rate) + n_delay_welding

                    if i_start != i_end:
                        buffer[-(i_end-i_start):] = wvf[i_start:i_end]

                    bias_T_compensation_mV = self._add_bias_T_compensation(buffer, bias_T_compensation_mV,
                                                                           section.sample_rate, channel_info)
                    self._upload_wvf(job, channel_name, buffer, channel_info.amplitude, channel_info.attenuation,
                                     section.sample_rate, awg_upload_func)

                    section = next_section
                    buffer = np.zeros(section.npt)

                    n_section = iround(t_welding*section.sample_rate) + iround(channel_info.delay_ns * section.sample_rate)
                    if iround(n_section*sample_rate/section.sample_rate) >= len(wvf):
                        raise Exception(f'segment {iseg} too short for welding. (nwelding:{n_section}, len_wvf:{len(wvf)})')

                    isub = [min(len(wvf)-1, i_end + iround(i*sample_rate/section.sample_rate)) for i in np.arange(n_section)]
                    welding_samples = np.take(wvf, isub)
                    buffer[:n_section] = welding_samples

                else:
                    if section != seg_render.section:
                        logging.error(f'OOPS-2 section mismatch {iseg}, {channel_name}')
                    offset = seg_render.offset + n_delay
                    buffer[offset+i_start:offset + len(wvf)] = wvf[i_start:]


            if job.neutralize:
                if section != sections[-1]:
                    # DC compensation is in a separate section
                    bias_T_compensation_mV = self._add_bias_T_compensation(buffer, bias_T_compensation_mV,
                                                                           section.sample_rate, channel_info)
                    self._upload_wvf(job, channel_name, buffer, channel_info.amplitude, channel_info.attenuation,
                                     section.sample_rate, awg_upload_func)
                    section = sections[-1]
                    buffer = np.zeros(section.npt)
                    logging.info(f'DC compensation section with {section.npt} Sa')

                compensation_npt = iround(job.upload_info.dc_compensation_duration * section.sample_rate)

                if compensation_npt > 0 and channel_info.dc_compensation:
                    compensation_voltage = -channel_info.integral * section.sample_rate / compensation_npt * 1e9
                    job.upload_info.dc_compensation_voltages[channel_name] = compensation_voltage
                    buffer[-(compensation_npt+1):-1] = compensation_voltage
                    logging.debug(f'DC compensation {channel_name}: {compensation_voltage:6.1f} mV {compensation_npt} Sa')
                else:
                    job.upload_info.dc_compensation_voltages[channel_name] = 0

            bias_T_compensation_mV = self._add_bias_T_compensation(buffer, bias_T_compensation_mV,
                                                                   section.sample_rate, channel_info)
            self._upload_wvf(job, channel_name, buffer, channel_info.amplitude, channel_info.attenuation,
                             section.sample_rate, awg_upload_func)
コード例 #7
0
    def _generate_sections(self, job):
        max_pre_start_ns = self.max_pre_start_ns
        max_post_end_ns = self.max_post_end_ns

        self.segments = []
        segments = self.segments
        t_start = 0
        for seg in job.sequence:
            # work with sample rate in GSa/s
            sample_rate = (seg.sample_rate if seg.sample_rate is not None else job.default_sample_rate) * 1e-9
            duration = seg.get_total_time(job.index)
            npt =  iround(duration * sample_rate)
            info = SegmentRenderInfo(sample_rate, t_start, npt)
            segments.append(info)
            t_start = info.t_end

        # sections
        sections = job.upload_info.sections
        t_start = -max_pre_start_ns
        nseg = len(segments)

        section = RenderSection(segments[0].sample_rate, t_start)
        sections.append(section)
        section.npt += iround(max_pre_start_ns * section.sample_rate)

        for iseg,seg in enumerate(segments):
            sample_rate = seg.sample_rate

            if iseg < nseg-1:
                sample_rate_next = segments[iseg+1].sample_rate
            else:
                sample_rate_next = 0

            # create welding region if sample_rate decreases
            if sample_rate < section.sample_rate:
                # welding region is length of padding for alignment + post_stop region
                n_post = iround(((seg.t_start + max_post_end_ns) - section.t_end) * section.sample_rate)
                section.npt += n_post
                section.align(extend=True)

                # number of points of segment to be rendered to previous section
                n_start_transition = iround((section.t_end - seg.t_start)*sample_rate)

                seg.n_start_transition = n_start_transition
                seg.start_section = section

                # start new section
                section = RenderSection(sample_rate, section.t_end)
                sections.append(section)
                section.npt -= n_start_transition


            seg.section = section
            seg.offset = section.npt
            section.npt += seg.npt

            # create welding region if sample rate increases
            if sample_rate_next != 0 and sample_rate_next > sample_rate:
                # The current section should end before the next segment starts:
                # - subtract any extension into the next segment
                # - align boundary with truncation
                n_pre = int(np.ceil((section.t_end - (seg.t_end - max_pre_start_ns)) * section.sample_rate))
                section.npt -= n_pre
                section.align(extend=False)

                # start new section
                section = RenderSection(sample_rate_next, section.t_end)
                sections.append(section)

                # number of points of segment to be rendered to next section
                n_end_transition = iround((seg.t_end - section.t_start)*sample_rate_next)

                section.npt += n_end_transition

                seg.n_end_transition = n_end_transition
                seg.end_section = section

        # add post stop samples; seg = last segment, section is last section
        n_post = iround(((seg.t_end + max_post_end_ns) - section.t_end) * section.sample_rate)
        section.npt += n_post

        # add DC compensation
        compensation_time = self.get_max_compensation_time()
        logging.debug(f'DC compensation time: {compensation_time*1e9} ns')
        compensation_npt = int(np.ceil(compensation_time * section.sample_rate * 1e9))
        if compensation_npt > 50_000:
            # more than 50_000 samples? Use new segment with lower sample rate for compensation

            sample_rate = 1e9 * section.sample_rate * 5_000 / compensation_npt
            # find an existing sample rate
            nice_sample_rates = [1e6, 2e6, 5e6, 1e7, 2e7, 5e7, 1e8, 2e8, 1e9]
            for sr in nice_sample_rates:
                if sample_rate <= sr:
                    sample_rate = sr * 1e-9
                    break
            # create new section
            section.align(extend=True)
            section = RenderSection(sample_rate, section.t_end)
            sections.append(section)
            # calculate npt
            compensation_npt = int(np.ceil(compensation_time * section.sample_rate * 1e9))
            logging.info(f'Added new segment for DC compensation: {int(compensation_time*1e9)} ns, '
                         f'sample_rate: {sr/1e6} MHz, {compensation_npt} Sa')

        job.upload_info.dc_compensation_duration = compensation_npt/section.sample_rate
        section.npt += compensation_npt

        # add at least 1 zero
        section.npt += 1
        section.align(extend=True)
        job.playback_time = section.t_end - sections[0].t_start
        job.n_waveforms = len(sections)
        logging.debug(f'Playback time: {job.playback_time} ns')

        if UploadAggregator.verbose:
            for segment in segments:
                logging.info(f'segment: {segment}')
            for section in sections:
                logging.info(f'section: {section}')