Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SD channel indexing improvements #92

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
78 changes: 51 additions & 27 deletions qcodes/instrument_drivers/Keysight/SD_common/SD_AWG.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,12 @@
from qcodes.instrument.channel import InstrumentChannel, ChannelList
from qcodes import validators as vals
from .SD_DIG import logclass
model_channel_idxs = {'M3201A': [0,1,2,3],
'M3300A': [0,1,2,3]}
model_channel_idxs = {'M3201A': [k+1 for k in range(4)],
'M3202A': [k+1 for k in range(4)],
'M3300A': [k+1 for k in range(4)],
'M3201A_legacy': [k for k in range(4)],
'M3300A_legacy': [k for k in range(4)],
}


class AWGChannel(InstrumentChannel):
Expand All @@ -21,31 +25,31 @@ def __init__(self, parent: Instrument, name: str, id: int, **kwargs):

# TODO: Joint amplitude and offset validation (-1.5<amp+offset<1.5)
self.add_parameter('amplitude',
label=f'ch{self.id} amplitude',
label=f'{self.name} amplitude',
unit='V',
set_function=self.awg.channelAmplitude,
docstring=f'ch{self.id} amplitude',
docstring=f'{self.name} amplitude',
vals=vals.Numbers(-1.5, 1.5))
self.add_parameter('offset',
label=f'ch{self.id} offset',
label=f'{self.name} offset',
unit='V',
set_function=self.awg.channelOffset,
docstring=f'The DC offset of ch{self.id}',
docstring=f'The DC offset of {self.name}',
vals=vals.Numbers(-1.5, 1.5))

self.add_parameter('wave_shape',
label=f'ch{self.id} wave shape',
label=f'{self.name} wave shape',
initial_value='arbitrary',
set_function=self.awg.channelWaveShape,
val_mapping={'HiZ': -1, 'none': 0, 'sinusoidal': 1,
'triangular': 2, 'square': 4, 'dc': 5,
'arbitrary': 6, 'partner_channel': 8},
docstring=f'The output waveform type of ch{self.id}. '
docstring=f'The output waveform type of {self.name}. '
f'Can be either arbitrary (AWG), or one '
f'of the function generator types.')

self.add_parameter('trigger_source',
label=f'ch{self.id} trigger source',
label=f'{self.name} trigger source',
val_mapping={'trig_in': 0,
**{f'pxi{k}': 4000+k for k in range(8)}},
initial_value='trig_in',
Expand All @@ -58,7 +62,7 @@ def __init__(self, parent: Instrument, name: str, id: int, **kwargs):
'trigger_direction == "in".')

self.add_parameter('trigger_mode',
label=f'ch{self.id} trigger mode',
label=f'{self.name} trigger mode',
initial_value='rising',
val_mapping={'active_high': 1, 'active_low': 2,
'rising': 3, 'falling': 4},
Expand All @@ -74,46 +78,46 @@ def __init__(self, parent: Instrument, name: str, id: int, **kwargs):
val_mapping={'one-shot': 0, 'cyclic': 1})

# Function generator parameters
self.add_parameter(f'frequency',
label=f'ch{self.id} frequency',
self.add_parameter('frequency',
label=f'{self.name} frequency',
unit='Hz',
set_function=self.awg.channelFrequency,
docstring=f'The frequency of ch{self.id}, only used '
docstring=f'The frequency of {self.name}, only used '
f'for the function generator (wave_shape '
f'is not arbitrary).',
vals=vals.Numbers(0, 200e6))
self.add_parameter(f'phase',
label=f'ch{self.id} phase',
self.add_parameter('phase',
label=f'{self.name} phase',
unit='deg',
set_function=self.awg.channelPhase,
docstring=f'The phase of ch{self.id}, only used '
docstring=f'The phase of {self.name}, only used '
f'for the function generator (wave_shape '
f'is not arbitrary).',
vals=vals.Numbers(0, 360))
self.add_parameter(f'IQ',
label=f'ch{self.id} IQ modulation',
self.add_parameter('IQ',
label=f'{self.name} IQ modulation',
val_mapping={'on': 1, 'off': 0},
set_function=self.awg.modulationIQconfig,
docstring=f'Enable or disable IQ modulation for '
f'ch{self.id}. If enabled, IQ modulation '
f'{self.name}. If enabled, IQ modulation '
f'will be applied to the function '
f'generator signal using the AWG.')
self.add_parameter(f'angle_modulation',
label=f'ch{self.id} angle modulation',
self.add_parameter('angle_modulation',
label=f'{self.name} angle modulation',
val_mapping={'none': 0, 'frequency': 1, 'phase': 2},
set_function=self.awg.modulationAngleConfig,
set_args=['angle_modulation', 'deviation_gain'],
docstring=f'Type of modulation to use for the '
f'function generator. Can be frequency or '
f'phase.')
self.add_parameter(f'deviation_gain',
label=f'ch{self.id} angle modulation',
self.add_parameter('deviation_gain',
label=f'{self.name} angle modulation',
vals=vals.Numbers(),
set_function=self.awg.modulationAngleConfig,
initial_value=0,
set_args=['angle_modulation', 'deviation_gain'],
docstring=f'Function generator modulation gain.'
f'To be used with angle_modulation')
docstring='Function generator modulation gain.'
'To be used with angle_modulation')

@with_error_check
def config_angle_modulation(self,
Expand Down Expand Up @@ -397,6 +401,7 @@ def __init__(self, name, model, chassis, slot, channel_idxs: List[int] = None, t
if channel_idxs is None:
channel_idxs = model_channel_idxs[self.model]
self.channel_idxs = channel_idxs
self.zero_based_channels = channel_idxs[0] == 0

# Create instance of keysight SD_AOU class
self.awg = logclass(keysightSD1.SD_AOU)()
Expand All @@ -420,7 +425,7 @@ def __init__(self, name, model, chassis, slot, channel_idxs: List[int] = None, t
vals=vals.Enum(0, 1))

self.add_parameter('trigger_direction',
label=f'trigger direction',
label='trigger direction',
val_mapping={'in': 1, 'out': 0},
set_function=self.awg.triggerIOconfig,
docstring='Determines if trig i/o should be used '
Expand All @@ -440,6 +445,13 @@ def __init__(self, name, model, chassis, slot, channel_idxs: List[int] = None, t
get_function=self.awg.clockGetSyncFrequency,
docstring='The frequency of the internal CLKsync in Hz')

if model == 'M3202A':
self._max_sample_rate = int(1e9)
# self.clock_frequency(200e6)
else:
self._max_sample_rate = int(500e6)
# self.clock_frequency(100e6)

channels = ChannelList(self,
name='channels',
chan_type=AWGChannel)
Expand Down Expand Up @@ -519,6 +531,8 @@ def reset_phase_channels(self, channels: List[int]):
reset_multiple_channel_phase(5) would reset the phase of channel 0 and 2
"""
# AWG channel mask, where LSB is for ch0, bit 1 is for ch1 etc.
if not self.zero_based_channels:
channels = [channel - 1 for channel in channels]
channel_mask = sum(2**channel for channel in channels)
return self.awg.channelPhaseResetMultiple(channel_mask)

Expand Down Expand Up @@ -642,6 +656,8 @@ def start_channels(self, channels: List[int]):
channels: List of channels to start
"""
# AWG channel mask, where LSB is for ch0, bit 1 is for ch1 etc.
if not self.zero_based_channels:
channels = [channel - 1 for channel in channels]
channel_mask = sum(2**channel for channel in channels)
self.awg.AWGstartMultiple(channel_mask)

Expand All @@ -654,6 +670,8 @@ def pause_channels(self, channels: List[int]):
channels: List of channels to pause
"""
# AWG channel mask, where LSB is for ch0, bit 1 is for ch1 etc.
if not self.zero_based_channels:
channels = [channel - 1 for channel in channels]
channel_mask = sum(2**channel for channel in channels)
self.awg.AWGpauseMultiple(channel_mask)

Expand All @@ -665,6 +683,8 @@ def resume_channels(self, channels: List[int]):
channels: List of channels to resume
"""
# AWG channel mask, where LSB is for ch0, bit 1 is for ch1 etc.
if not self.zero_based_channels:
channels = [channel - 1 for channel in channels]
channel_mask = sum(2**channel for channel in channels)
self.awg.AWGresumeMultiple(channel_mask)

Expand All @@ -678,6 +698,8 @@ def stop_channels(self, channels: List[int]):
channels: List of channels to stop
"""
# AWG channel mask, where LSB is for ch0, bit 1 is for ch1 etc.
if not self.zero_based_channels:
channels = [channel - 1 for channel in channels]
channel_mask = sum(2**channel for channel in channels)
self.awg.AWGstopMultiple(channel_mask)

Expand All @@ -688,9 +710,11 @@ def trigger_channels(self, channels: List[int]):
provided it is configured with VI/HVI Trigger.

Args:
channels: List of chnanels to trigger
channels: List of channels to trigger
"""
# AWG channel mask, where LSB is for ch0, bit 1 is for ch1 etc.
if not self.zero_based_channels:
channels = [channel - 1 for channel in channels]
maij marked this conversation as resolved.
Show resolved Hide resolved
channel_mask = sum(2**channel for channel in channels)
self.awg.AWGtriggerMultiple(channel_mask)

Expand Down
39 changes: 26 additions & 13 deletions qcodes/instrument_drivers/Keysight/SD_common/SD_DIG.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@


# Functions to log method calls from the SD_AIN class
from typing import List
import re, sys, types
def logmethod(value):
def method_wrapper(self, *args, **kwargs):
Expand Down Expand Up @@ -38,8 +39,8 @@ def logclass(cls):
return cls


model_channels = {'M3300A': 8}

model_channel_idxs = {'M3300A_legacy': [k for k in range(8)],
'M3300A': [k+1 for k in range(8)]}

class DigitizerChannel(InstrumentChannel):
"""Signadyne digitizer channel
Expand All @@ -48,13 +49,16 @@ class DigitizerChannel(InstrumentChannel):
parent: Parent Signadyne digitizer Instrument
name: channel name (e.g. 'ch1')
id: channel id (e.g. 1)
zero_based: Whether channels are zero-based.
Newer models have 1-based channels.
**kwargs: Additional kwargs passed to InstrumentChannel
"""
def __init__(self, parent: Instrument, name: str, id: int, **kwargs):
def __init__(self, parent: Instrument, name: str, id: int, zero_based: bool, **kwargs):
super().__init__(parent=parent, name=name, **kwargs)

self.SD_AIN = self._parent.SD_AIN
self.id = id
self.id_zero_based = id if zero_based else id - 1

# For channelInputConfig
self.add_parameter(
Expand Down Expand Up @@ -121,7 +125,7 @@ def __init__(self, parent: Instrument, name: str, id: int, **kwargs):
set_function=self.SD_AIN.DAQconfig,
set_args=['points_per_cycle', 'n_cycles',
'trigger_delay_samples', 'trigger_mode'],
docstring=f'The number of cycles to collect on DAQ {self.id}'
docstring=f'The number of cycles to collect on ch{self.id}'
)

self.add_parameter(
Expand Down Expand Up @@ -323,21 +327,22 @@ def __init__(self,
model: str,
chassis: int,
slot: int,
channels: int = None,
channel_idxs: List[int] = None,
triggers: int = 8,
**kwargs):
super().__init__(name, model, chassis, slot, triggers, **kwargs)

if channels is None:
channels = model_channels[self.model]
if channel_idxs is None:
channel_idxs = model_channel_idxs[self.model]

# Create instance of keysight SD_AIN class
# We wrap it in a logclass so that any method call is recorded in
# self.SD_AIN._method_calls
self.SD_AIN = logclass(keysightSD1.SD_AIN)()

# store card-specifics
self.n_channels = channels
self.channel_idxs = channel_idxs
self.zero_based_channels = channel_idxs[0] == 0

# Open the device, using the specified chassis and slot number
self.initialize(chassis=chassis, slot=slot)
Expand Down Expand Up @@ -380,11 +385,11 @@ def __init__(self,
docstring='The trigger input value, 0 (OFF) or 1 (ON)',
val_mapping={'off': 0, 'on': 1})

channels = ChannelList(self,
name='channels',
chan_type=DigitizerChannel)
for ch in range(self.n_channels):
channel = DigitizerChannel(self, name=f'ch{ch}', id=ch)
channels = ChannelList(self, name='channels', chan_type=DigitizerChannel)

# Channel.id_zero_based needs to know whether channels idxs are zero-based
for ch in self.channel_idxs:
channel = DigitizerChannel(self, name=f'ch{ch}', id=ch, zero_based=self.zero_based_channels)
setattr(self, f'ch{ch}', channel)
channels.append(channel)

Expand Down Expand Up @@ -430,6 +435,8 @@ def start_channels(self, channels: List[int]):

"""
# DAQ channel mask, where LSB is for DAQ_0, bit 1 is for DAQ_1 etc.
if not self.zero_based_channels:
channels = [ch-1 for ch in channels]
channel_mask = sum(2**channel for channel in channels)
return self.SD_AIN.DAQstartMultiple(channel_mask)

Expand All @@ -444,6 +451,8 @@ def stop_channels(self, channels: List[int]):
AssertionError if DAQstopMultiple was unsuccessful
"""
# DAQ channel mask, where LSB is for DAQ_0, bit 1 is for DAQ_1 etc.
if not self.zero_based_channels:
channels = [ch-1 for ch in channels]
channel_mask = sum(2**channel for channel in channels)
return self.SD_AIN.DAQstopMultiple(channel_mask)

Expand All @@ -459,6 +468,8 @@ def trigger_channels(self, channels):
"""

# DAQ channel mask, where LSB is for DAQ_0, bit 1 is for DAQ_1 etc.
if not self.zero_based_channels:
channels = [ch-1 for ch in channels]
channel_mask = sum(2**channel for channel in channels)
return self.SD_AIN.DAQtriggerMultiple(channel_mask)

Expand All @@ -473,6 +484,8 @@ def flush_channels(self, channels: List[int]):
AssertionError if DAQflushMultiple was unsuccessful
"""
# DAQ channel mask, where LSB is for DAQ_0, bit 1 is for DAQ_1 etc.
if not self.zero_based_channels:
channels = [ch-1 for ch in channels]
channel_mask = sum(2**channel for channel in channels)
return self.SD_AIN.DAQflushMultiple(channel_mask)

Expand Down
3 changes: 2 additions & 1 deletion qcodes/instrument_drivers/Keysight/SD_common/SD_FPGA.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ def __init__(self, name, model, chassis, slot, **kwargs):
# Open the device, using the specified chassis and slot number
FPGA_name = self.SD_module.getProductNameBySlot(chassis, slot) + '_FPGA'
if isinstance(FPGA_name, str):
result_code = self.SD_module.openWithSlot(FPGA_name, chassis, slot)
result_code = self.SD_module.openWithSlotCompatibility(FPGA_name, chassis, slot, compatibility=1
)
if result_code <= 0:
raise Exception('Could not open FPGA '
'error code {}'.format(result_code))
Expand Down
6 changes: 3 additions & 3 deletions qcodes/instrument_drivers/Keysight/SD_common/SD_Module.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,11 +193,11 @@ def __init__(self, name, model, chassis, slot, triggers, **kwargs):
# Open the device, using the specified chassis and slot number
module_name = self.SD_module.getProductNameBySlot(chassis, slot)
if isinstance(module_name, str):
result_code = self.SD_module.openWithSlot(module_name, chassis, slot)
result_code = self.SD_module.openWithSlotCompatibility(module_name, chassis, slot, compatibility=1)
if result_code <= 0:
raise Exception('Could not open SD_Module error code {result_code}')
raise Exception(f'Could not open SD_Module error code {result_code}')
else:
raise Exception('No SD Module found at chassis {chassis}, slot {slot}')
raise Exception(f'Found {module_name} at chassis {chassis}, slot {slot} instead of SD module')

for i in range(triggers):
self.add_parameter('pxi_trigger_number_{}'.format(i),
Expand Down
Loading