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 QRM in mind, but also 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 pprint
import os
import json
import ipywidgets as widgets
from ipywidgets import interact

#Add Pulsar QRM interface
from pulsar_qrm.pulsar_qrm import pulsar_qrm

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

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

#Reset the instrument for good measure.
pulsar.reset()
print("Status:")
print(pulsar.get_system_status())
Status:
{'status': 'OKAY', '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
wave_and_prog_dict = {"waveforms": {}, "weights": {}, "acquisitions": {}, "program": "stop"}
with open("sequence.json", 'w', encoding='utf-8') as file:
    json.dump(wave_and_prog_dict, file, indent=4)
    file.close()
pulsar.sequencer0_waveforms_and_program(os.path.join(os.getcwd(), "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:
{'status': 'STOPPED', 'flags': ['FORCED STOP']}
Snapshot:
qrm:
        parameter                                  value
--------------------------------------------------------------------------------
IDN                                         :   {'manufacturer': 'Qblox', 'mode...
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
sequencer0_channel_map_path0_out0_en        :   True
sequencer0_channel_map_path1_out1_en        :   True
sequencer0_cont_mode_en_awg_path0           :   False
sequencer0_cont_mode_en_awg_path1           :   False
sequencer0_cont_mode_waveform_idx_awg_path0 :   0
sequencer0_cont_mode_waveform_idx_awg_path1 :   0
sequencer0_demod_en_acq                     :   False
sequencer0_discretization_threshold_acq     :   0
sequencer0_gain_awg_path0                   :   1
sequencer0_gain_awg_path1                   :   1
sequencer0_integration_length_acq           :   1024
sequencer0_marker_ovr_en                    :   False
sequencer0_marker_ovr_value                 :   0
sequencer0_mixer_corr_gain_ratio            :   0.50002
sequencer0_mixer_corr_phase_offset_degree   :   -0
sequencer0_mod_en_awg                       :   True
sequencer0_nco_freq                         :   1e+07 (Hz)
sequencer0_nco_phase_offs                   :   0 (Degrees)
sequencer0_offset_awg_path0                 :   1
sequencer0_offset_awg_path1                 :   1
sequencer0_phase_rotation_acq               :   0 (Degrees)
sequencer0_sync_en                          :   False
sequencer0_upsample_rate_awg_path0          :   0
sequencer0_upsample_rate_awg_path1          :   0
sequencer0_waveforms_and_program            :   /home/jordy/qblox/projects/puls...
sequencer1_channel_map_path0_out0_en        :   True
sequencer1_channel_map_path1_out1_en        :   True
sequencer1_cont_mode_en_awg_path0           :   False
sequencer1_cont_mode_en_awg_path1           :   False
sequencer1_cont_mode_waveform_idx_awg_path0 :   0
sequencer1_cont_mode_waveform_idx_awg_path1 :   0
sequencer1_demod_en_acq                     :   False
sequencer1_discretization_threshold_acq     :   0
sequencer1_gain_awg_path0                   :   1
sequencer1_gain_awg_path1                   :   1
sequencer1_integration_length_acq           :   1024
sequencer1_marker_ovr_en                    :   False
sequencer1_marker_ovr_value                 :   0
sequencer1_mixer_corr_gain_ratio            :   1
sequencer1_mixer_corr_phase_offset_degree   :   -0
sequencer1_mod_en_awg                       :   False
sequencer1_nco_freq                         :   0 (Hz)
sequencer1_nco_phase_offs                   :   0 (Degrees)
sequencer1_offset_awg_path0                 :   0
sequencer1_offset_awg_path1                 :   0
sequencer1_phase_rotation_acq               :   0 (Degrees)
sequencer1_sync_en                          :   False
sequencer1_upsample_rate_awg_path0          :   0
sequencer1_upsample_rate_awg_path1          :   0
sequencer1_waveforms_and_program            :   None
sequencer2_channel_map_path0_out0_en        :   True
sequencer2_channel_map_path1_out1_en        :   True
sequencer2_cont_mode_en_awg_path0           :   False
sequencer2_cont_mode_en_awg_path1           :   False
sequencer2_cont_mode_waveform_idx_awg_path0 :   0
sequencer2_cont_mode_waveform_idx_awg_path1 :   0
sequencer2_demod_en_acq                     :   False
sequencer2_discretization_threshold_acq     :   0
sequencer2_gain_awg_path0                   :   1
sequencer2_gain_awg_path1                   :   1
sequencer2_integration_length_acq           :   1024
sequencer2_marker_ovr_en                    :   False
sequencer2_marker_ovr_value                 :   0
sequencer2_mixer_corr_gain_ratio            :   1
sequencer2_mixer_corr_phase_offset_degree   :   -0
sequencer2_mod_en_awg                       :   False
sequencer2_nco_freq                         :   0 (Hz)
sequencer2_nco_phase_offs                   :   0 (Degrees)
sequencer2_offset_awg_path0                 :   0
sequencer2_offset_awg_path1                 :   0
sequencer2_phase_rotation_acq               :   0 (Degrees)
sequencer2_sync_en                          :   False
sequencer2_upsample_rate_awg_path0          :   0
sequencer2_upsample_rate_awg_path1          :   0
sequencer2_waveforms_and_program            :   None
sequencer3_channel_map_path0_out0_en        :   True
sequencer3_channel_map_path1_out1_en        :   True
sequencer3_cont_mode_en_awg_path0           :   False
sequencer3_cont_mode_en_awg_path1           :   False
sequencer3_cont_mode_waveform_idx_awg_path0 :   0
sequencer3_cont_mode_waveform_idx_awg_path1 :   0
sequencer3_demod_en_acq                     :   False
sequencer3_discretization_threshold_acq     :   0
sequencer3_gain_awg_path0                   :   1
sequencer3_gain_awg_path1                   :   1
sequencer3_integration_length_acq           :   1024
sequencer3_marker_ovr_en                    :   False
sequencer3_marker_ovr_value                 :   0
sequencer3_mixer_corr_gain_ratio            :   1
sequencer3_mixer_corr_phase_offset_degree   :   -0
sequencer3_mod_en_awg                       :   False
sequencer3_nco_freq                         :   0 (Hz)
sequencer3_nco_phase_offs                   :   0 (Degrees)
sequencer3_offset_awg_path0                 :   0
sequencer3_offset_awg_path1                 :   0
sequencer3_phase_rotation_acq               :   0 (Degrees)
sequencer3_sync_en                          :   False
sequencer3_upsample_rate_awg_path0          :   0
sequencer3_upsample_rate_awg_path1          :   0
sequencer3_waveforms_and_program            :   None
sequencer4_channel_map_path0_out0_en        :   True
sequencer4_channel_map_path1_out1_en        :   True
sequencer4_cont_mode_en_awg_path0           :   False
sequencer4_cont_mode_en_awg_path1           :   False
sequencer4_cont_mode_waveform_idx_awg_path0 :   0
sequencer4_cont_mode_waveform_idx_awg_path1 :   0
sequencer4_demod_en_acq                     :   False
sequencer4_discretization_threshold_acq     :   0
sequencer4_gain_awg_path0                   :   1
sequencer4_gain_awg_path1                   :   1
sequencer4_integration_length_acq           :   1024
sequencer4_marker_ovr_en                    :   False
sequencer4_marker_ovr_value                 :   0
sequencer4_mixer_corr_gain_ratio            :   1
sequencer4_mixer_corr_phase_offset_degree   :   -0
sequencer4_mod_en_awg                       :   False
sequencer4_nco_freq                         :   0 (Hz)
sequencer4_nco_phase_offs                   :   0 (Degrees)
sequencer4_offset_awg_path0                 :   0
sequencer4_offset_awg_path1                 :   0
sequencer4_phase_rotation_acq               :   0 (Degrees)
sequencer4_sync_en                          :   False
sequencer4_upsample_rate_awg_path0          :   0
sequencer4_upsample_rate_awg_path1          :   0
sequencer4_waveforms_and_program            :   None
sequencer5_channel_map_path0_out0_en        :   True
sequencer5_channel_map_path1_out1_en        :   True
sequencer5_cont_mode_en_awg_path0           :   False
sequencer5_cont_mode_en_awg_path1           :   False
sequencer5_cont_mode_waveform_idx_awg_path0 :   0
sequencer5_cont_mode_waveform_idx_awg_path1 :   0
sequencer5_demod_en_acq                     :   False
sequencer5_discretization_threshold_acq     :   0
sequencer5_gain_awg_path0                   :   1
sequencer5_gain_awg_path1                   :   1
sequencer5_integration_length_acq           :   1024
sequencer5_marker_ovr_en                    :   False
sequencer5_marker_ovr_value                 :   0
sequencer5_mixer_corr_gain_ratio            :   1
sequencer5_mixer_corr_phase_offset_degree   :   -0
sequencer5_mod_en_awg                       :   False
sequencer5_nco_freq                         :   0 (Hz)
sequencer5_nco_phase_offs                   :   0 (Degrees)
sequencer5_offset_awg_path0                 :   0
sequencer5_offset_awg_path1                 :   0
sequencer5_phase_rotation_acq               :   0 (Degrees)
sequencer5_sync_en                          :   False
sequencer5_upsample_rate_awg_path0          :   0
sequencer5_upsample_rate_awg_path1          :   0
sequencer5_waveforms_and_program            :   None