Skip to content

Commit

Permalink
driver: Add block averaged reads and python headers to M4i (microsoft…
Browse files Browse the repository at this point in the history
…#588)

* added blockread function to M4i

* verbose=0 by default

* autopep; add docs; make output of blockavg similar to other output

* added multichannel acquisition functionality to all acquisition functions

* add docs

* add README for M4i header files

* make channel readout faster

* fix whitespace
  • Loading branch information
peendebak authored and giulioungaretti committed May 12, 2017
1 parent 1ac2146 commit d25b35a
Show file tree
Hide file tree
Showing 7 changed files with 3,074 additions and 17 deletions.
136 changes: 119 additions & 17 deletions qcodes/instrument_drivers/Spectrum/M4i.py
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,7 @@ def __init__(self, name, cardid='spcm0', **kwargs):
set_cmd=partial(self._set_param32bit,
pyspcm.SPC_CARDMODE),
vals=Enum(pyspcm.SPC_REC_STD_SINGLE, pyspcm.SPC_REC_STD_MULTI, pyspcm.SPC_REC_STD_GATE, pyspcm.SPC_REC_STD_ABA,
pyspcm.SPC_REC_FIFO_SINGLE, pyspcm.SPC_REC_FIFO_MULTI, pyspcm.SPC_REC_FIFO_GATE, pyspcm.SPC_REC_FIFO_ABA),
pyspcm.SPC_REC_FIFO_SINGLE, pyspcm.SPC_REC_FIFO_MULTI, pyspcm.SPC_REC_FIFO_GATE, pyspcm.SPC_REC_FIFO_ABA, pyspcm.SPC_REC_STD_AVERAGE),
docstring='defines the used operating mode')

# wait command
Expand Down Expand Up @@ -546,6 +546,9 @@ def __init__(self, name, cardid='spcm0', **kwargs):
pyspcm.SPC_M2CMD),
docstring='executes a command for the card or data transfer')

# memsize used for simple channel read-out
self._channel_memsize = 2**12

# checks if requirements for the compensation get and set functions are met
def _get_compensation(self, i):
# if HF enabled
Expand All @@ -565,6 +568,11 @@ def _set_compensation(self, i, value):
logging.warning(
"M4i: HF path not set, ignoring ACDC offset compensation set\n")

def active_channels(self):
""" Return a list with the indices of the active channels """
x = bin(self.enable_channels())[2:]
return [i for i in range(len(x)) if x[i]]

def get_idn(self):
return dict(zip(('vendor', 'model', 'serial', 'firmware'), ('Spectrum_GMBH', szTypeToName(self.get_card_type()), self.serial_number(), ' ')))

Expand All @@ -573,32 +581,59 @@ def convert_to_voltage(self, data, input_range):
resolution = self.ADC_to_voltage()
return data * input_range / resolution

def initialize_channels(self, channels=None, mV_range=1000, input_path=0, termination=0, coupling=0, compensation=None):
def initialize_channels(self, channels=None, mV_range=1000, input_path=0,
termination=0, coupling=0, compensation=None, memsize=2**12):
""" Setup channels of the digitizer for simple readout using Parameters
The channels can be read out using the Parmeters `channel_0`, `channel_1`, ...
Args:
channels (list): list of channels to setup
mV_range, input_path, termination, coupling, compensation: passed to the set_channel_settings function
mV_range, input_path, termination, coupling, compensation: passed
to the set_channel_settings function
memsize (int): memory size to use for simple channel readout
"""
allchannels = 0
self._channel_memsize = memsize
if channels is None:
channels = range(4)
for ch in channels:
self.set_channel_settings(ch, mV_range, input_path=input_path,
termination=termination, coupling=coupling, compensation=compensation)
self.enable_channels(getattr(pyspcm, 'CHANNEL%d' % ch))
allchannels = allchannels + getattr(pyspcm, 'CHANNEL%d' % ch)

self.enable_channels(allchannels)

def _read_channel(self, channel, memsize=2**11):
def _channel_mask(self, channels=range(4)):
""" Return mask for specified channels
Args:
channels (list): list of channel indices
Returns:
cx (int): channel mask
"""
cx = 0
for c in channels:
cx += getattr(pyspcm, 'CHANNEL{}'.format(c))
return cx

def _read_channel(self, channel, memsize=None):
""" Helper function to read out a channel
Before a channel is measured it is explicitly enabled.
Before a channel is measured all channels are enabled to ensure we can
read out channels without the overhead of changing channels.
"""
if memsize is None:
memsize = self._channel_memsize
posttrigger_size = int(memsize / 2)
mV_range = getattr(self, 'range_channel_%d' % channel).get()
self.enable_channels(getattr(pyspcm, 'CHANNEL{}'.format(channel)))
value = np.mean(self.single_software_trigger_acquisition(
mV_range, memsize, posttrigger_size))
cx = self._channel_mask()
self.enable_channels(cx)
data = self.single_software_trigger_acquisition(
mV_range, memsize, posttrigger_size)
active = self.active_channels()
data = data.reshape((-1, len(active)))
value = np.mean(data[:, channel])
return value

def set_channel_settings(self, i, mV_range, input_path, termination, coupling, compensation=None):
Expand Down Expand Up @@ -656,18 +691,19 @@ def multiple_trigger_acquisition(self, mV_range, memsize, seg_size, posttrigger_
self.data_memory_size(memsize)
self.segment_size(seg_size)
self.posttrigger_memory_size(posttrigger_size)
numch = bin(self.enable_channels()).count("1")

self.general_command(pyspcm.M2CMD_CARD_START |
pyspcm.M2CMD_CARD_ENABLETRIGGER | pyspcm.M2CMD_CARD_WAITREADY)

# setup software buffer
buffer_size = ct.c_int16 * memsize
buffer_size = ct.c_int16 * memsize * numch
data_buffer = (buffer_size)()
data_pointer = ct.cast(data_buffer, ct.c_void_p)

# data acquisition
self._def_transfer64bit(
pyspcm.SPCM_BUF_DATA, pyspcm.SPCM_DIR_CARDTOPC, 0, data_pointer, 0, 2 * memsize)
pyspcm.SPCM_BUF_DATA, pyspcm.SPCM_DIR_CARDTOPC, 0, data_pointer, 0, 2 * memsize * numch)
self.general_command(pyspcm.M2CMD_DATA_STARTDMA |
pyspcm.M2CMD_DATA_WAITDMA)

Expand All @@ -688,18 +724,19 @@ def single_trigger_acquisition(self, mV_range, memsize, posttrigger_size):
# set memsize and posttrigger
self.data_memory_size(memsize)
self.posttrigger_memory_size(posttrigger_size)
numch = bin(self.enable_channels()).count("1")

self.general_command(pyspcm.M2CMD_CARD_START |
pyspcm.M2CMD_CARD_ENABLETRIGGER | pyspcm.M2CMD_CARD_WAITREADY)

# setup software buffer
buffer_size = ct.c_int16 * memsize
buffer_size = ct.c_int16 * memsize * numch
data_buffer = (buffer_size)()
data_pointer = ct.cast(data_buffer, ct.c_void_p)

# data acquisition
self._def_transfer64bit(
pyspcm.SPCM_BUF_DATA, pyspcm.SPCM_DIR_CARDTOPC, 0, data_pointer, 0, 2 * memsize)
pyspcm.SPCM_BUF_DATA, pyspcm.SPCM_DIR_CARDTOPC, 0, data_pointer, 0, 2 * memsize * numch)
self.general_command(pyspcm.M2CMD_DATA_STARTDMA |
pyspcm.M2CMD_DATA_WAITDMA)

Expand All @@ -725,18 +762,19 @@ def gated_trigger_acquisition(self, mV_range, memsize, pretrigger_size, posttrig
self.data_memory_size(memsize)
self.pretrigger_memory_size(pretrigger_size)
self.posttrigger_memory_size(posttrigger_size)
numch = bin(self.enable_channels()).count("1")

self.general_command(pyspcm.M2CMD_CARD_START |
pyspcm.M2CMD_CARD_ENABLETRIGGER | pyspcm.M2CMD_CARD_WAITREADY)

# setup software buffer
buffer_size = ct.c_int16 * memsize
buffer_size = ct.c_int16 * memsize * numch
data_buffer = (buffer_size)()
data_pointer = ct.cast(data_buffer, ct.c_void_p)

# data acquisition
self._def_transfer64bit(
pyspcm.SPCM_BUF_DATA, pyspcm.SPCM_DIR_CARDTOPC, 0, data_pointer, 0, 2 * memsize)
pyspcm.SPCM_BUF_DATA, pyspcm.SPCM_DIR_CARDTOPC, 0, data_pointer, 0, 2 * memsize * numch)
self.general_command(pyspcm.M2CMD_DATA_STARTDMA |
pyspcm.M2CMD_DATA_WAITDMA)

Expand All @@ -751,31 +789,95 @@ def gated_trigger_acquisition(self, mV_range, memsize, pretrigger_size, posttrig
return voltages

def single_software_trigger_acquisition(self, mV_range, memsize, posttrigger_size):
""" Acquire a single data trace
Args:
mV_range
memsize (int): size of data trace
posttrigger_size (int): size of data trace after triggering
Returns:
voltages (array)
"""
self.card_mode(pyspcm.SPC_REC_STD_SINGLE) # single

self.data_memory_size(memsize)
self.posttrigger_memory_size(posttrigger_size)
numch = bin(self.enable_channels()).count("1")

# start/enable trigger/wait ready
self.trigger_or_mask(pyspcm.SPC_TMASK_SOFTWARE) # software trigger
self.general_command(pyspcm.M2CMD_CARD_START |
pyspcm.M2CMD_CARD_ENABLETRIGGER | pyspcm.M2CMD_CARD_WAITREADY)

# setup software buffer
buffer_size = ct.c_int16 * memsize
buffer_size = ct.c_int16 * memsize * numch
data_buffer = (buffer_size)()
data_pointer = ct.cast(data_buffer, ct.c_void_p)

# data acquisition
self._def_transfer64bit(
pyspcm.SPCM_BUF_DATA, pyspcm.SPCM_DIR_CARDTOPC, 0, data_pointer, 0, 2 * memsize)
pyspcm.SPCM_BUF_DATA, pyspcm.SPCM_DIR_CARDTOPC, 0, data_pointer, 0, 2 * memsize * numch)
self.general_command(pyspcm.M2CMD_DATA_STARTDMA |
pyspcm.M2CMD_DATA_WAITDMA)

# convert buffer to numpy array
data = ct.cast(data_pointer, ct.POINTER(buffer_size))
output = np.frombuffer(data.contents, dtype=ct.c_int16)
self._debug = output
self._stop_acquisition()

voltages = self.convert_to_voltage(output, mV_range / 1000)

return voltages

def blockavg_hardware_trigger_acquisition(self, mV_range, nr_averages=10, verbose=0):
""" Acquire data using block averaging and hardware triggering
To read out multiple channels, use `initialize_channels`
Args:
mV_range
nr_averages (int): number of averages to take
verbose (int): output level
Returns:
voltages (array): if multiple channels are read, then the data is interleaved
"""
# self.available_card_modes()
if nr_averages < 2:
raise Exception('averaging for less than 2 times is not supported')
self.card_mode(pyspcm.SPC_REC_STD_AVERAGE) # single
memsize = self.data_memory_size()
self.segment_size(memsize)
self._set_param32bit(pyspcm.SPC_AVERAGES, nr_averages)
numch = bin(self.enable_channels()).count("1")

if verbose:
print('blockavg_hardware_trigger_acquisition: errors %s' %
(self.get_error_info32bit(), ))
print('blockavg_hardware_trigger_acquisition: card_status %s' %
(self.card_status(), ))

self.external_trigger_mode(pyspcm.SPC_TM_POS)
self.trigger_or_mask(pyspcm.SPC_TMASK_EXT0)
self.general_command(pyspcm.M2CMD_CARD_START |
pyspcm.M2CMD_CARD_ENABLETRIGGER | pyspcm.M2CMD_CARD_WAITREADY)

# setup software buffer
sizeof32bit = 4
buffer_size = ct.c_int32 * memsize * numch
data_buffer = (buffer_size)()
data_pointer = ct.cast(data_buffer, ct.c_void_p)

# data acquisition
self._def_transfer64bit(
pyspcm.SPCM_BUF_DATA, pyspcm.SPCM_DIR_CARDTOPC, 0, data_pointer, 0, sizeof32bit * memsize * numch)
self.general_command(pyspcm.M2CMD_DATA_STARTDMA |
pyspcm.M2CMD_DATA_WAITDMA)

# convert buffer to numpy array
data = ct.cast(data_pointer, ct.POINTER(buffer_size))
output = np.frombuffer(data.contents, dtype=ct.c_int32) / nr_averages
self._debug = output

self._stop_acquisition()

Expand Down
3 changes: 3 additions & 0 deletions qcodes/instrument_drivers/Spectrum/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
The files `pyspcm.py` and the files in `py_header` are copied from the Spectrum M4i digitizer installation. See http://spectrum-instrumentation.com/en/m4i-platform-overview


Empty file.
Loading

0 comments on commit d25b35a

Please sign in to comment.