Keysight M3202A AWG example with digitizer

The Keysight M3202A is a 1 GSa/a arbitrary waveform generator.

An instance of M3202A represents a module with 4 output channels. The channels of one module share the uploaded waveforms and trigers.

This example loads loads waveforms in AWGs in slots 2 and 3 of chassis 0.

This example uses a digitizer from the keysightSD1 module to read the generated signals. The digitizer is in slot 5. The output channels 1 and 2 of the AWG in slot 2 should be connected to inputs 1 and 2 of the digitizer and output channels 3 and 4 of the AWG slot 3 should be conencted to inputs 3 and 4 of the digitizer.

[ ]:
import logging

import numpy as np
import matplotlib.pyplot as plt

try:
    import keysightSD1
except:
    # add the path where the keysight library probably resides and try again
    import sys
    sys.path.append(r'C:\Program Files (x86)\Keysight\SD1\Libraries\Python')
    import keysightSD1

import qcodes
from qcodes_contrib_drivers.drivers.Keysight.M3202A import M3202A

import qcodes.logger as logger
from qcodes.logger import start_all_logging

start_all_logging()
# logger.get_file_handler().setLevel(logging.DEBUG)

[ ]:
# try to close station from previous run.
try:
    station.close_all_registered_instruments()
except: pass
[ ]:
# Demonstration of some errors
try:
    wrong_chassis = M3202A("wrong chassis", chassis = 5, slot = 16)
except Exception as e:
    print('Expected failure:', e)

try:
    empty_slot = M3202A("empty_slot", chassis = 0, slot = 16)
except Exception as e:
    print('Expected failure:', e)

try:
    not_M3202A = M3202A("Digitizer", chassis = 0, slot = 5)
except Exception as e:
    print('Expected failure:', e)
[ ]:
awg1 = M3202A("AWG1", chassis = 0, slot = 2)
awg2 = M3202A("AWG2", chassis = 0, slot = 3)

station = qcodes.Station()

station.add_component(awg1)
station.add_component(awg2)

[ ]:
# utility functions to create waveforms

def get_divider(prescaler):
    if prescaler == 0:
        divider = 1
    elif prescaler == 1:
        divider = 5
    else:
        divider = prescaler * 10

    return divider

def create_sawtooth(period, repetition, prescaler):
    divider = get_divider(prescaler)
    n_pts = period // divider
    w = np.linspace(-1, 1, n_pts)
    w = np.tile(w, repetition)

    if len(w) < 2000:
        raise Exception('not enough data')

    return w

def create_sine(period, repetition, prescaler):
    divider = get_divider(prescaler)
    n_pts = repetition * period // divider
    phi = np.linspace(0, np.pi*2*repetition, n_pts)
    w = np.sin(phi)

    if len(w) < 2000:
        raise Exception('not enough data')

    return w
[ ]:
# setup output channels

pxi1 = keysightSD1.SD_TriggerExternalSources.TRIGGER_PXI1
trigger_mode = keysightSD1.SD_TriggerBehaviors.TRIGGER_FALL

for awg in [awg1, awg2]:
    for ch in range(1,5):
        awg.set_channel_offset(0.0, ch)
        awg.set_channel_amplitude(1.0, ch)

        awg.set_channel_wave_shape(keysightSD1.SD_Waveshapes.AOU_AWG, ch)
        awg.awg_queue_config(ch, keysightSD1.SD_QueueMode.CYCLIC)
        awg.awg_config_external_trigger(ch, pxi1, trigger_mode)

awg2.amplitude_channel_1.set(0.5)
awg2.offset_channel_2.set(0.5)

Generate and enqueue waveforms

Signal duration 20 us: * “AWG1.1”: 20x sawtooth (followed by zeros) * “AWG1.2”: 20 steps (followed by zeros) * “AWG2.3”: 2 us zero, 4 us sine, 2 us zero, 12 us sine * “AWG2.4”: 4 us sine, 4 us zero, 8 us sine, 4 us zero

[ ]:
prescaler_1GSa = 0
prescaler_200MSa = 1
prescaler_50MSa = 2

# 2000 zero's for end
zeros = np.zeros(2000)

# sawtooth period = 1 us: 2 period in 1 sample
sawtooth_2us = 0.5 * create_sawtooth(1000, 2, prescaler_1GSa)
# 20 steps use prescaler to reduce total number of samples. 200MSa/s => 4000 pts; 200 per step
steps = np.concatenate([0.02*i*np.ones(200) for i in range(20)])

# sine wave 2 MHz, 4 periods
sine = 0.6 * create_sine(500, 4, prescaler_1GSa)

# start all uploads
zeros_awg1 = awg1.upload_waveform(zeros)
sawtooth_2us_awg1 = awg1.upload_waveform(sawtooth_2us)
steps_awg1 = awg1.upload_waveform(steps)

zeros_awg2 = awg2.upload_waveform(zeros)
sine_awg2 = awg2.upload_waveform(sine)

# enqueue wave references
delay = 0
ext_trigger = keysightSD1.SD_TriggerModes.EXTTRIG
auto_trigger = keysightSD1.SD_TriggerModes.AUTOTRIG

awg1.awg_queue_waveform(1, sawtooth_2us_awg1, ext_trigger, delay, 10, prescaler_1GSa)
awg1.awg_queue_waveform(1, zeros_awg1, auto_trigger, delay, 1, prescaler_1GSa)

awg1.awg_queue_waveform(2, steps_awg1, ext_trigger, delay, 1, prescaler_200MSa)
awg1.awg_queue_waveform(2, zeros_awg1, auto_trigger, delay, 1, prescaler_1GSa)

awg2.awg_queue_waveform(3, zeros_awg2, ext_trigger, delay, 1, prescaler_1GSa)
awg2.awg_queue_waveform(3, sine_awg2, auto_trigger, delay, 2, prescaler_1GSa)
awg2.awg_queue_waveform(3, zeros_awg2, auto_trigger, delay, 1, prescaler_1GSa)
awg2.awg_queue_waveform(3, sine_awg2, auto_trigger, delay, 6, prescaler_1GSa)

awg2.awg_queue_waveform(4, sine_awg2, ext_trigger, delay, 2, prescaler_1GSa)
awg2.awg_queue_waveform(4, zeros_awg2, auto_trigger, delay, 2, prescaler_1GSa)
awg2.awg_queue_waveform(4, sine_awg2, auto_trigger, delay, 4, prescaler_1GSa)
awg2.awg_queue_waveform(4, zeros_awg2, auto_trigger, delay, 2, prescaler_1GSa)

# start AWGs. They will wait for external trigger.
awg1.awg_start_multiple(0b0011)
awg2.awg_start_multiple(0b1100)

configure digitizer to capture signals

[ ]:


def check_error(error, s=''): if (type(error) is int and error < 0): print(keysightSD1.SD_Error.getErrorMessage(error), error, s) PRODUCT = "" CHASSIS = 0 # change slot numbers to your values SLOT_IN = 5 NUM_CHANNELS = 4 # CREATE AND OPEN MODULE IN moduleIn = keysightSD1.SD_AIN() moduleInID = moduleIn.openWithSlot(PRODUCT, CHASSIS, SLOT_IN) NUM_CYCLES = 1 DIG_PRESCALER = 0 # sample 21 us IN_DURATION = 21 TOT_POINTS_IN = 500 * IN_DURATION FULL_SCALE = 2.5 DELAY_IN = 200 // (DIG_PRESCALER + 1) mask = 0 for ch in range(1,5): moduleIn.DAQstop(ch) moduleIn.DAQflush(ch) for c in range(0, NUM_CHANNELS): mask |= 1 << c ch = c + 1 # MODULE IN moduleIn.channelInputConfig(ch, FULL_SCALE, keysightSD1.AIN_Impedance.AIN_IMPEDANCE_HZ, keysightSD1.AIN_Coupling.AIN_COUPLING_DC) moduleIn.channelPrescalerConfig(ch, DIG_PRESCALER) moduleIn.DAQdigitalTriggerConfig(ch, pxi1, trigger_mode) moduleIn.DAQconfig(ch, TOT_POINTS_IN, 1, DELAY_IN, ext_trigger) moduleIn.DAQstartMultiple(mask)

Trigger AWGs and digitizer

[ ]:
# start AWGs and digitizers via PXI
# Note: PXI trigger can be set via any module
awg1.set_pxi_trigger(0, pxi1)
awg1.set_pxi_trigger(1, pxi1)

Retrieve digitizer data and plot result

[ ]:

POINTS_PER_READ = 20000 READ_TIMEOUT = 1000 numReadPoints = [] dataPoints = [] for c in range(0, NUM_CHANNELS): numReadPoints.append(0) dataPoints.append(np.empty(0, dtype=np.short)) readDone = False print() print("Reading data...") cnt = 0 while not readDone: if (cnt > 10): print('no more data') break readDone = True for c in range(0, NUM_CHANNELS): npts = moduleIn.DAQcounterRead(c + 1) if npts <= 0: print(f'channel {c} counter {npts} {numReadPoints[c]}') cnt += 1 else: readPoints = moduleIn.DAQread(c + 1, min(POINTS_PER_READ, npts), READ_TIMEOUT) check_error(readPoints) if type(readPoints) is int or readPoints.size == 0: cnt += 1 else: cnt = 0 dataPoints[c] = np.append(dataPoints[c], readPoints) numReadPoints[c] += readPoints.size print(f'channel {c} counter {npts} read {readPoints.size} ({numReadPoints[c]})') readDone = readDone and (numReadPoints[c] >= TOT_POINTS_IN) for i in range(1,5): fig = plt.figure(i) fig.clear() plt.plot(dataPoints[i-1]) plt.show()