Download
Download this file as Jupyter notebook: waveforms.ipynb.
Introduction to RF waveforms
This guide demonstrates how to generate
RFWaveform
s using pulse
Envelope
s as introduced in Introduction to pulse envelopes.
An RFWaveform
specifies a duration, an
amplitude, a target frequency, an
instantaneous phase, and a post-increment phase, which are used to modulate (or
upconvert) a base Envelope
to address a system at a
specific frequency. The post-increment phase is frequently used to implement a virtual
Z gate.
[2]:
# initialize a Gaussian envelope
import keysight.qcs as qcs
import numpy as np
gauss_envelope = qcs.GaussianEnvelope(num_sigma=3)
# initialize an RF waveform with that envelope
rf_waveform = qcs.RFWaveform(
duration=1,
envelope=gauss_envelope,
amplitude=1,
rf_frequency=10,
instantaneous_phase=np.pi / 3,
post_phase=np.pi,
)
# instantiate a channel to play an AWG on
awg = qcs.Channels(range(1), "awg")
# initialize the program and add the waveform
program = qcs.Program()
program.add_waveform(rf_waveform, awg)
# render
program.render(sample_rate=1e3)
The arguments rf_frequency
, instantaneous_phase
, and post_phase
can also
be Scalar
s, which allows their values to be
modified later on.
[3]:
# initialize variables for the frequency and phases
rf_frequency = qcs.Scalar("frequency", value=10, dtype=float)
instantaneous_phase = qcs.Scalar("instantaneous_phase", value=np.pi, dtype=float)
post_phase = qcs.Scalar("post_phase", value=np.pi, dtype=float)
# initialize RF waveforms
rf_waveform = qcs.RFWaveform(
1,
gauss_envelope,
1,
rf_frequency=rf_frequency,
instantaneous_phase=instantaneous_phase,
post_phase=post_phase,
)
# to illustrate how phases accumulate, we add a constant RF waveform
constant_rf = qcs.RFWaveform(1, qcs.ConstantEnvelope(), 1, rf_frequency)
# instantiate another channel to play an AWG on
awg2 = qcs.Channels([2], "awg")
# initialize the program and add the waveforms
program = qcs.Program()
program.add_waveform([rf_waveform, constant_rf], awg)
# add only the constant waveform to awg2, and delay it by the duration of the RF
program.add_waveform(constant_rf, awg2, pre_delay=rf_waveform.duration)
# render
program.render(sample_rate=1e3, channel_subplots=False)
Note that the constant waveform after the RF waveform has a different starting phase
in the above two plots. This is because the starting phase of each waveform in a
waveform sequence is determined by the previous waveforms. Specifically, each waveform
is multiplied by the product of the outputs of the
phase_update()
for all preceding RF
waveforms.
We now change the value of instantaneous_phase
and re-render the program. Note
that changing instantaneous_phase
only changes the phase of the Gaussian waveform.
[4]:
instantaneous_phase.value = 0.0
program.render(sample_rate=1e3, channel_subplots=False)
We now change the value of post_phase
and re-render the program. Note that
changing post_phase
only changes the phase of the constant waveform after the
Gaussian waveform.
[5]:
post_phase.value = np.pi / 2
program.render(sample_rate=1e3, channel_subplots=False)
Signal generation background
We now describe how RF signals are generated.
The output \(V(t)\) of the AWG at time \(t\) for a single waveform is
where \(E(t)\) is the envelope, \(\omega\) is the angular frequency, and \(\phi\) is a phase.
A waveform specifies the signal in a time interval \([0, T]\) where \(T\) is
the duration of the waveform. When it is in a program, the time can be translated in
one of two ways, which is handled automatically based on the
keysight.qcs.channels.Channels.absolute_phase
property:
The time at the start of the waveform can be treated as \(t = 0\) (
absolute_phase == True
).The time at the start of the waveform can be set to the end of the time of the previous operation (
absolute_phase == False
).
In order to explain how these translations are handled, we now explain the two-stage process for producing high frequency signals:
We modulate by an intermediate frequency \(\omega_{IF}\) on the FPGA, taking a complex signal \(E_0(t)\) to the complex signal
\[E_\alpha(t) = E_0(t) \exp[i (\omega_{IF} t + \phi_{\rm DUC})]\]where \(\phi_{\rm DUC}\) is the phase of the FPGA upconversion.
The signal at the intermediate frequency is converted up to the target frequency on an ASIC, giving the output signal
\[V(t) = \mathrm{Re}\left( E_0(t) \exp[i (\omega t + \phi_{\rm DUC} + \phi_{\rm LO})]\right)\]where \(\phi_{\rm LO}\) is the phase of the local oscillator on the ASIC.
When an RF waveform is used to stimulate a system and the response is downconverted by a downconverter with the same local oscillator frequency and phase, the value of the local oscillator phase will not affect the output signal. Thus, we only need to account for \(\phi_{\rm DUC}\). This phase is determined by the start time.
Download
Download this file as Jupyter notebook: waveforms.ipynb.