See also

An IPython notebook version of this tutorial can be downloaded here:

mixer_correction.ipynb

Mixer correction

In this simple tutorial we will demonstrate the ability to compensate for output mixer non-idealities and observe the changes using an oscilloscope.

Mixer non-idealities can lead to unwanted spurs on the output (LO/RF/IF feedthrough and other spurious products) and they can be compensated by applying adjustments to the I/Q outputs: phase offset, gain ratio and DC offset. This solution applies to both baseband QCM/QRM products using external mixers as well as QCM-RF and QRM-RF products.

The tutorial is designed with a baseband Pulsar in mind, but can easily be used with all Qblox baseband products: Pulsar QRM, Pulsar QCM, Cluster QRM, Cluster QCM. We will adjust all the parameters listed above and observe the changes to the I/Q outputs directly on an oscilloscope.

The tutorial can also work with QCM-RF and QRM-RF products, but the effects can only be observed indirectly by connecting a signal analyzer to the mixed output.

Requirements: - Oscilloscope - Install ipywidgets: “pip install ipywidgets” - Changes to notebook extensions: “jupyter nbextension enable –py widgetsnbextension”

[1]:
#Set up environment.
import json
import ipywidgets as widgets
from ipywidgets import interact

from qblox_instruments import Pulsar

#Close any existing connections to any Pulsar.
Pulsar.close_all()

#Connect to the Pulsar at default IP address.
pulsar = Pulsar("pulsar", "192.168.0.2")

#Reset the instrument for good measure.
pulsar.reset()
print("Status:")
print(pulsar.get_system_state())
Status:
SystemState(status=<SystemStatus.OKAY: 2>, flags=[])

Setup

The easiest way to view the influence of the mixer correction is to mix the NCO sin and cos with I and Q values of 1 (fullscale). The instrument output would be simple sinusoids with a 90deg phase offset and identical amplitude.

We use sequencer 0 to set I and Q values of 1 (fullscle) using DC offset and we mix those with the NCO signals.

[2]:
#Program sequence we will not use.
sequence = {"waveforms": {}, "weights": {}, "acquisitions": {}, "program": "stop"}
with open("sequence.json", 'w', encoding='utf-8') as file:
    json.dump(sequence, file, indent=4)
    file.close()
pulsar.sequencer0.sequence("sequence.json")

#Program fullscale DC offset on I & Q, turn on NCO and enable modulation.
pulsar.sequencer0.offset_awg_path0(1.0)
pulsar.sequencer0.offset_awg_path1(1.0)
pulsar.sequencer0.nco_freq(10e6)
pulsar.sequencer0.mod_en_awg(True)

Control sliders

Create control sliders for the parameters described in the introduction. Each time the value of a parameter is updated, the sequencer is automatically stopped from the embedded firmware for safety reasons and has to be manually restarted.

The sliders cover the valid parameter range. If the code below is modified to input invalid values, the Pulsar firmware will not program the values.

Please connect the I/Q outputs to an oscilloscope and set to trigger continuously on the I channel at 0V. Execute the code below, move the sliders and observe the result on the oscilloscope.

[3]:
def set_offset_I(offset_I):
    pulsar.out0_offset(offset_I)
    pulsar.arm_sequencer(0)
    pulsar.start_sequencer(0)

def set_offset_Q(offset_Q):
    pulsar.out1_offset(offset_Q)
    pulsar.arm_sequencer(0)
    pulsar.start_sequencer(0)

def set_gain_ratio(gain_ratio):
    pulsar.sequencer0.mixer_corr_gain_ratio(gain_ratio)
    pulsar.arm_sequencer(0)
    pulsar.start_sequencer(0)

def set_phase_offset(phase_offset):
    pulsar.sequencer0.mixer_corr_phase_offset_degree(phase_offset)
    pulsar.arm_sequencer(0)
    pulsar.start_sequencer(0)

interact(set_offset_I, offset_I=widgets.FloatSlider(min=-1.0, max=1.0, step=0.01, start=0.0))
interact(set_offset_Q, offset_Q=widgets.FloatSlider(min=-1.0, max=1.0, step=0.01, start=0.0))
interact(set_gain_ratio, gain_ratio=widgets.FloatSlider(min=0.5, max=2.0, step=0.1, start=0.5))
interact(set_phase_offset, phase_offset=widgets.FloatSlider(min=-45.0, max=45.0, step=1.0, start=0.0))
[3]:
<function __main__.set_phase_offset(phase_offset)>

Stop

Finally, let’s stop the sequencer and close the instrument connection.

[4]:
#Stop sequencers.
pulsar.stop_sequencer()

#Print sequencer status (should now say it is stopped).
print("Status:")
print(pulsar.get_sequencer_state(0))

#Print an overview of the instrument parameters.
print("Snapshot:")
pulsar.print_readable_snapshot(update=True)

#Close the instrument connection.
pulsar.close()
Status:
SequencerState(status=<SequencerStatus.STOPPED: 5>, flags=[<SequencerStatusFlags.FORCED_STOP: 2>])
Snapshot:
pulsar:
        parameter                    value
--------------------------------------------------------------------------------
IDN                           : {'manufacturer': 'qblox', 'model': 'pulsar_qr...
in0_gain                      : -6 (dB)
in1_gain                      : -6 (dB)
out0_offset                   : 0 (V)
out1_offset                   : 0 (V)
reference_source              : internal
scope_acq_avg_mode_en_path0   : False
scope_acq_avg_mode_en_path1   : False
scope_acq_sequencer_select    : 0
scope_acq_trigger_level_path0 : 0
scope_acq_trigger_level_path1 : 0
scope_acq_trigger_mode_path0  : sequencer
scope_acq_trigger_mode_path1  : sequencer
pulsar_sequencer0:
        parameter                       value
--------------------------------------------------------------------------------
channel_map_path0_out0_en        :      True
channel_map_path1_out1_en        :      True
cont_mode_en_awg_path0           :      False
cont_mode_en_awg_path1           :      False
cont_mode_waveform_idx_awg_path0 :      0
cont_mode_waveform_idx_awg_path1 :      0
demod_en_acq                     :      False
discretization_threshold_acq     :      0
gain_awg_path0                   :      1
gain_awg_path1                   :      1
integration_length_acq           :      1024
marker_ovr_en                    :      False
marker_ovr_value                 :      0
mixer_corr_gain_ratio            :      0.50002
mixer_corr_phase_offset_degree   :      -0
mod_en_awg                       :      True
nco_freq                         :      1e+07 (Hz)
nco_phase_offs                   :      0 (Degrees)
offset_awg_path0                 :      1
offset_awg_path1                 :      1
phase_rotation_acq               :      0 (Degrees)
sequence                         :      sequence.json
sync_en                          :      False
upsample_rate_awg_path0          :      0
upsample_rate_awg_path1          :      0
pulsar_sequencer1:
        parameter                       value
--------------------------------------------------------------------------------
channel_map_path0_out0_en        :      True
channel_map_path1_out1_en        :      True
cont_mode_en_awg_path0           :      False
cont_mode_en_awg_path1           :      False
cont_mode_waveform_idx_awg_path0 :      0
cont_mode_waveform_idx_awg_path1 :      0
demod_en_acq                     :      False
discretization_threshold_acq     :      0
gain_awg_path0                   :      1
gain_awg_path1                   :      1
integration_length_acq           :      1024
marker_ovr_en                    :      False
marker_ovr_value                 :      0
mixer_corr_gain_ratio            :      1
mixer_corr_phase_offset_degree   :      -0
mod_en_awg                       :      False
nco_freq                         :      0 (Hz)
nco_phase_offs                   :      0 (Degrees)
offset_awg_path0                 :      0
offset_awg_path1                 :      0
phase_rotation_acq               :      0 (Degrees)
sequence                         :      None
sync_en                          :      False
upsample_rate_awg_path0          :      0
upsample_rate_awg_path1          :      0
pulsar_sequencer2:
        parameter                       value
--------------------------------------------------------------------------------
channel_map_path0_out0_en        :      True
channel_map_path1_out1_en        :      True
cont_mode_en_awg_path0           :      False
cont_mode_en_awg_path1           :      False
cont_mode_waveform_idx_awg_path0 :      0
cont_mode_waveform_idx_awg_path1 :      0
demod_en_acq                     :      False
discretization_threshold_acq     :      0
gain_awg_path0                   :      1
gain_awg_path1                   :      1
integration_length_acq           :      1024
marker_ovr_en                    :      False
marker_ovr_value                 :      0
mixer_corr_gain_ratio            :      1
mixer_corr_phase_offset_degree   :      -0
mod_en_awg                       :      False
nco_freq                         :      0 (Hz)
nco_phase_offs                   :      0 (Degrees)
offset_awg_path0                 :      0
offset_awg_path1                 :      0
phase_rotation_acq               :      0 (Degrees)
sequence                         :      None
sync_en                          :      False
upsample_rate_awg_path0          :      0
upsample_rate_awg_path1          :      0
pulsar_sequencer3:
        parameter                       value
--------------------------------------------------------------------------------
channel_map_path0_out0_en        :      True
channel_map_path1_out1_en        :      True
cont_mode_en_awg_path0           :      False
cont_mode_en_awg_path1           :      False
cont_mode_waveform_idx_awg_path0 :      0
cont_mode_waveform_idx_awg_path1 :      0
demod_en_acq                     :      False
discretization_threshold_acq     :      0
gain_awg_path0                   :      1
gain_awg_path1                   :      1
integration_length_acq           :      1024
marker_ovr_en                    :      False
marker_ovr_value                 :      0
mixer_corr_gain_ratio            :      1
mixer_corr_phase_offset_degree   :      -0
mod_en_awg                       :      False
nco_freq                         :      0 (Hz)
nco_phase_offs                   :      0 (Degrees)
offset_awg_path0                 :      0
offset_awg_path1                 :      0
phase_rotation_acq               :      0 (Degrees)
sequence                         :      None
sync_en                          :      False
upsample_rate_awg_path0          :      0
upsample_rate_awg_path1          :      0
pulsar_sequencer4:
        parameter                       value
--------------------------------------------------------------------------------
channel_map_path0_out0_en        :      True
channel_map_path1_out1_en        :      True
cont_mode_en_awg_path0           :      False
cont_mode_en_awg_path1           :      False
cont_mode_waveform_idx_awg_path0 :      0
cont_mode_waveform_idx_awg_path1 :      0
demod_en_acq                     :      False
discretization_threshold_acq     :      0
gain_awg_path0                   :      1
gain_awg_path1                   :      1
integration_length_acq           :      1024
marker_ovr_en                    :      False
marker_ovr_value                 :      0
mixer_corr_gain_ratio            :      1
mixer_corr_phase_offset_degree   :      -0
mod_en_awg                       :      False
nco_freq                         :      0 (Hz)
nco_phase_offs                   :      0 (Degrees)
offset_awg_path0                 :      0
offset_awg_path1                 :      0
phase_rotation_acq               :      0 (Degrees)
sequence                         :      None
sync_en                          :      False
upsample_rate_awg_path0          :      0
upsample_rate_awg_path1          :      0
pulsar_sequencer5:
        parameter                       value
--------------------------------------------------------------------------------
channel_map_path0_out0_en        :      True
channel_map_path1_out1_en        :      True
cont_mode_en_awg_path0           :      False
cont_mode_en_awg_path1           :      False
cont_mode_waveform_idx_awg_path0 :      0
cont_mode_waveform_idx_awg_path1 :      0
demod_en_acq                     :      False
discretization_threshold_acq     :      0
gain_awg_path0                   :      1
gain_awg_path1                   :      1
integration_length_acq           :      1024
marker_ovr_en                    :      False
marker_ovr_value                 :      0
mixer_corr_gain_ratio            :      1
mixer_corr_phase_offset_degree   :      -0
mod_en_awg                       :      False
nco_freq                         :      0 (Hz)
nco_phase_offs                   :      0 (Degrees)
offset_awg_path0                 :      0
offset_awg_path1                 :      0
phase_rotation_acq               :      0 (Degrees)
sequence                         :      None
sync_en                          :      False
upsample_rate_awg_path0          :      0
upsample_rate_awg_path1          :      0