From 18aa323f1826cb0c798212a15915b49ea2825173 Mon Sep 17 00:00:00 2001 From: Natalie Pearson Date: Tue, 2 Aug 2016 18:47:49 +0200 Subject: [PATCH 01/17] feature: initial commit for ATS9360 alazar driver. Generalise ATS.py and debugging. Changes to example notebook. Add specific ATS9360.py file for board settings. nb in this commit there is no use of the acquisition controller --- docs/examples/Qcodes example ATS_ONWORK.ipynb | 219 +++++++++++--- qcodes/instrument_drivers/AlazarTech/ATS.py | 234 ++++++++++----- .../instrument_drivers/AlazarTech/ATS9360.py | 282 ++++++++++++++++++ .../AlazarTech/ATS_acquisition_controllers.py | 13 +- 4 files changed, 639 insertions(+), 109 deletions(-) create mode 100644 qcodes/instrument_drivers/AlazarTech/ATS9360.py diff --git a/docs/examples/Qcodes example ATS_ONWORK.ipynb b/docs/examples/Qcodes example ATS_ONWORK.ipynb index dd3004c89bb7..206812252a0a 100644 --- a/docs/examples/Qcodes example ATS_ONWORK.ipynb +++ b/docs/examples/Qcodes example ATS_ONWORK.ipynb @@ -7,6 +7,14 @@ "collapsed": false }, "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "C:\\Anaconda3\\lib\\site-packages\\IPython\\kernel\\__init__.py:13: ShimWarning: The `IPython.kernel` package has been deprecated. You should import from ipykernel or jupyter_client instead.\n", + " \"You should import from ipykernel or jupyter_client instead.\", ShimWarning)\n" + ] + }, { "data": { "application/javascript": [ @@ -357,6 +365,8 @@ "import time\n", "import numpy as np\n", "\n", + "from imp import reload\n", + "\n", "import qcodes as qc\n", "\n", "qc.halt_bg()\n", @@ -373,21 +383,32 @@ "metadata": { "collapsed": false }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "import qcodes.instrument_drivers.AlazarTech.ATS9870 as ATSdriver\n", - "import qcodes.instrument_drivers.AlazarTech.ATS_acquisition_controllers as ats_contr" + "import qcodes.instrument_drivers.AlazarTech.ATS9360 as ATSdriver; reload(ATSdriver)\n", + "#import qcodes.instrument_drivers.AlazarTech.ATS_acquisition_controllers as ats_contr" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { - "collapsed": true + "collapsed": false }, "outputs": [], "source": [ - "contr1 = ats_contr.DFT_AcquisitionController(demodulation_frequency=10e6)" + "#contr1 = ats_contr.DFT_AcquisitionController(demodulation_frequency=10e6)" ] }, { @@ -398,7 +419,7 @@ }, "outputs": [], "source": [ - "ats_inst = ATSdriver.AlazarTech_ATS9870(name='Alazar1')" + "ats_inst = ATSdriver.AlazarTech_ATS9360(name='Alazar1', server_name=None)" ] }, { @@ -407,43 +428,143 @@ "metadata": { "collapsed": false }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "AlazarSetCaptureClock\n", + "512\n", + "\n", + "AlazarInputControl\n", + "512\n", + "\n", + "AlazarInputControl\n", + "512\n", + "\n", + "AlazarSetTriggerOperation\n", + "512\n", + "\n", + "AlazarSetExternalTrigger\n", + "512\n", + "\n", + "AlazarSetTriggerDelay\n", + "512\n", + "\n", + "AlazarSetTriggerTimeOut\n", + "512\n" + ] + } + ], "source": [ "ats_inst.config(clock_source='INTERNAL_CLOCK',\n", - " sample_rate=1000000,\n", + " sample_rate=500000000,\n", " clock_edge='CLOCK_EDGE_RISING',\n", " decimation=0,\n", - " coupling=['AC','AC'],\n", - " channel_range=[2.,2.],\n", - " impedance=[50,50],\n", - " bwlimit=['DISABLED','DISABLED'],\n", + " coupling=['DC','DC'],\n", + " channel_range=[0.4, 0.4],\n", " trigger_operation='TRIG_ENGINE_OP_J',\n", " trigger_engine1='TRIG_ENGINE_J',\n", " trigger_source1='EXTERNAL',\n", " trigger_slope1='TRIG_SLOPE_POSITIVE',\n", - " trigger_level1=0,\n", + " trigger_level1=150,\n", " trigger_engine2='TRIG_ENGINE_K',\n", - " trigger_source2='CHANNEL_B',\n", + " trigger_source2='DISABLE',\n", " trigger_slope2='TRIG_SLOPE_POSITIVE',\n", - " trigger_level2=0,\n", - " external_trigger_coupling='AC',\n", - " external_trigger_range='ETR_1V',\n", + " trigger_level2=128,\n", + " external_trigger_coupling='DC',\n", + " external_trigger_range='ETR_5V',\n", " trigger_delay=0,\n", " timeout_ticks=0)" ] }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 6, "metadata": { - "collapsed": false + "collapsed": false, + "scrolled": false }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "AlazarAbortAsyncRead\n", + "512\n", + "\n", + "AlazarSetRecordSize\n", + "512\n", + "\n", + "AlazarBeforeAsyncRead\n", + "512\n", + "\n", + "AlazarGetChannelInfo\n", + "512\n", + "bytes per sample 2\n", + "samples_per_record is 1024\n", + "bytes per record is 2048\n", + "samples_per_buffer is 10240\n", + "bytes_per_buffer is 40960\n", + "buffers cleared\n", + "2allocated buffers\n", + "making DMA buffer\n", + "making DMA buffer\n", + "made buffer list length 2\n", + "138543104\n", + "\n", + "\n", + "AlazarPostAsyncBuffer\n", + "512\n", + "138608640\n", + "\n", + "\n", + "AlazarPostAsyncBuffer\n", + "512\n", + "completed AlazarPostAsyncBuffer\n", + "\n", + "AlazarStartCapture\n", + "512\n", + "Capturing 3 buffers.\n", + "\n", + "AlazarWaitAsyncBufferComplete\n", + "518\n", + "[31136 36320 31360 ..., 28736 32480 28832]\n", + "\n", + "AlazarPostAsyncBuffer\n", + "518\n", + "\n", + "AlazarWaitAsyncBufferComplete\n", + "518\n", + "[31136 36320 31360 ..., 28736 32480 28832]\n", + "\n", + "AlazarPostAsyncBuffer\n", + "518\n", + "\n", + "AlazarWaitAsyncBufferComplete\n", + "518\n", + "[31136 36320 31360 ..., 28736 32480 28832]\n", + "\n", + "AlazarPostAsyncBuffer\n", + "518\n", + "\n", + "AlazarAbortAsyncRead\n", + "512\n", + "buffers cleared\n", + "Capture completed in 0.026781 sec\n", + "Captured 3 buffers (112.018308 buffers per sec)\n", + "Captured 30 records (1120.183076 records per sec)\n", + "Transferred 122880 bytes (4588269.880869 bytes per sec)\n" + ] + } + ], "source": [ - "output = ats_inst.acquire(mode='NPT',\n", + "ats_inst.acquire(mode='NPT',\n", " samples_per_record=1024,\n", - " records_per_buffer=70,\n", - " buffers_per_acquisition=128,\n", + " records_per_buffer=10,\n", + " buffers_per_acquisition=3,\n", " channel_selection='AB',\n", " transfer_offset=0,\n", " external_startcapture='DISABLED',\n", @@ -452,35 +573,59 @@ " fifo_only_streaming='DISABLED',\n", " interleave_samples='DISABLED',\n", " get_processed_data='ENABLED',\n", - " allocated_buffers=None,\n", - " buffer_timeout=1000,\n", - " acquisition_controller=contr1)" + " allocated_buffers=2,\n", + " buffer_timeout=10,\n", + " acquisition_controller=None)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "buffers cleared\n" + ] + } + ], + "source": [ + "ats_inst.clear_buffers()" ] }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 6, "metadata": { "collapsed": false }, "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "AlazarGetChannelInfo\n", + "512\n" + ] + }, { "data": { "text/plain": [ - "[1.2225695082720591,\n", - " 1.216472639837185,\n", - " -179.9999999998654,\n", - " -179.99999999986107,\n", - " 359.9999999999957]" + "{'bits_per_sample': 12, 'board': 'ATS9360', 'max_samples': 4294967294}" ] }, - "execution_count": 9, + "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "output" + "ats_inst.get_idn()" ] }, { @@ -496,9 +641,9 @@ "metadata": { "anaconda-cloud": {}, "kernelspec": { - "display_name": "Python [Root]", + "display_name": "Python 3", "language": "python", - "name": "Python [Root]" + "name": "python3" }, "language_info": { "codemirror_mode": { @@ -510,7 +655,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.5.2" + "version": "3.5.1" } }, "nbformat": 4, diff --git a/qcodes/instrument_drivers/AlazarTech/ATS.py b/qcodes/instrument_drivers/AlazarTech/ATS.py index 53dcb11d24c3..463664163c94 100644 --- a/qcodes/instrument_drivers/AlazarTech/ATS.py +++ b/qcodes/instrument_drivers/AlazarTech/ATS.py @@ -1,6 +1,7 @@ import ctypes import logging import numpy as np +import time import os from qcodes.instrument.base import Instrument @@ -10,7 +11,6 @@ # TODO (C) logging # these items are important for generalizing this code to multiple alazar cards -# TODO (W) remove 8 bits per sample requirement # TODO (W) some alazar cards have a different number of channels :( # this driver only works with 2-channel cards @@ -18,6 +18,11 @@ # acquisition that would overflow the board if measurement is not stopped # quickly enough. can this be solved by not reposting the buffers? +# TODO: check 8 bit sample thing works +# TODO: test get_board_info and find_boards +# TODO: remove guerilla debuggung 'print' statements +# TODO: call_dll error handling +# TODO: acquisition_controller class AlazarTech_ATS(Instrument): # override dll_path in your init script or in the board constructor @@ -188,8 +193,13 @@ def get_board_info(cls, dll, system_id, board_id): def __init__(self, name, system_id=1, board_id=1, dll_path=None, **kwargs): super().__init__(name, **kwargs) - self._ATS_dll = ctypes.cdll.LoadLibrary(dll_path or self.dll_path) - + self._ATS_dll = None + + if os.name == 'nt': + self._ATS_dll = ctypes.cdll.LoadLibrary(dll_path or self.dll_path) + else: + raise Exception("Unsupported OS") + # TODO (W) make the board id more general such that more than one card # per system configurations are supported self._handle = self._ATS_dll.AlazarGetBoardBySystemID(system_id, @@ -201,6 +211,14 @@ def __init__(self, name, system_id=1, board_id=1, dll_path=None, **kwargs): # TODO (M) do something with board kind here self.buffer_list = [] + + # TODO use get_board_info for this and make it return better stuff! + def get_idn(self): + board_kind = self._board_names[self._ATS_dll.AlazarGetBoardKind(self._handle)] + max_s, bps = self._get_channel_info(self._handle) + return {'board' : board_kind, + 'max_samples' : max_s, + 'bits_per_sample' : bps} def config(self, clock_source=None, sample_rate=None, clock_edge=None, decimation=None, coupling=None, channel_range=None, @@ -277,9 +295,10 @@ def config(self, clock_source=None, sample_rate=None, clock_edge=None, self.parameters['coupling' + str(i)], self.parameters['channel_range' + str(i)], self.parameters['impedance' + str(i)]) - self._call_dll('AlazarSetBWLimit', - self._handle, i, - self.parameters['bwlimit' + str(i)]) + if bwlimit is not None: + self._call_dll('AlazarSetBWLimit', + self._handle, i, + self.parameters['bwlimit' + str(i)]) self._call_dll('AlazarSetTriggerOperation', self._handle, self.trigger_operation, @@ -339,7 +358,7 @@ def acquire(self, mode=None, samples_per_record=None, self._set_if_present('samples_per_record', samples_per_record) self._set_if_present('records_per_buffer', records_per_buffer) self._set_if_present('buffers_per_acquisition', - buffers_per_acquisition) + buffers_per_acquisition), self._set_if_present('channel_selection', channel_selection) self._set_if_present('transfer_offset', transfer_offset) self._set_if_present('external_startcapture', external_startcapture) @@ -362,12 +381,7 @@ def acquire(self, mode=None, samples_per_record=None, # Abort any previous measurement self._call_dll('AlazarAbortAsyncRead', self._handle) - - # get channel info - max_s, bps = self._get_channel_info(self._handle) - if bps != 8: - raise Exception('Only 8 bits per sample supported at this moment') - + # Set record size for NPT mode if mode == 'NPT': pretriggersize = 0 # pretriggersize is 0 for NPT always @@ -375,11 +389,8 @@ def acquire(self, mode=None, samples_per_record=None, self._handle, pretriggersize, self.samples_per_record) - # set acquisition parameters here for NPT, TS mode - if self.channel_selection._get_byte() == 3: - number_of_channels = 2 - else: - number_of_channels = 1 + + # set acquisition parameters here for NPT, TS mode samples_per_buffer = 0 buffers_per_acquisition = self.buffers_per_acquisition._get_byte() samples_per_record = self.samples_per_record._get_byte() @@ -393,8 +404,7 @@ def acquire(self, mode=None, samples_per_record=None, if mode == 'NPT': records_per_buffer = self.records_per_buffer._get_byte() - records_per_acquisition = ( - records_per_buffer * buffers_per_acquisition) + records_per_acquisition = records_per_buffer * buffers_per_acquisition samples_per_buffer = samples_per_record * records_per_buffer self._call_dll('AlazarBeforeAsyncRead', @@ -435,38 +445,76 @@ def acquire(self, mode=None, samples_per_record=None, self.interleave_samples._set_updated() self.get_processed_data._set_updated() + + # bytes per sample + handle = self._handle + max_s, bps = self._get_channel_info(handle) + bytes_per_sample = (bps + 7) // 8 + print("bytes per sample " +str(bytes_per_sample)) + + # bytes per record + bytes_per_record = bytes_per_sample * samples_per_record + print("samples_per_record is "+str(samples_per_record)) + print("bytes per record is "+str(bytes_per_record)) + + # bytes per buffer + if self.channel_selection._get_byte() == 3: + number_of_channels = 2 + else: + number_of_channels = 1 + + bytes_per_buffer = bytes_per_record * records_per_buffer * number_of_channels + # create buffers for acquisition + # TODO: should this be > 1 (makes sense to me) or > 8 as in alazar sample code? - Natalie + sample_type = ctypes.c_uint8 + if bytes_per_sample > 1: + sample_type = ctypes.c_uint16 + + print("samples_per_buffer is "+str(samples_per_buffer)) + print("bytes_per_buffer is "+str(bytes_per_buffer)) + self.clear_buffers() allocated_buffers = self.allocated_buffers._get_byte() + print(str(allocated_buffers)+"allocated buffers") for k in range(allocated_buffers): try: - self.buffer_list.append(Buffer(bps, samples_per_buffer, - number_of_channels)) + self.buffer_list.append(DMABuffer(sample_type, bytes_per_buffer)) except: self.clear_buffers() raise # post buffers to Alazar + print("made buffer list length "+str(len(self.buffer_list))) for buf in self.buffer_list: + print(buf.addr) + print(type(buf.size_bytes)) + self._ATS_dll.AlazarPostAsyncBuffer.argtypes = [ctypes.c_uint32, ctypes.c_void_p, ctypes.c_uint32] self._call_dll('AlazarPostAsyncBuffer', self._handle, buf.addr, buf.size_bytes) self.allocated_buffers._set_updated() + print("completed AlazarPostAsyncBuffer") # -----start capture here----- - acquisition_controller.pre_start_capture(self) +# acquisition_controller.pre_start_capture(self) + start = time.clock() # Keep track of when acquisition started # call the startcapture method self._call_dll('AlazarStartCapture', self._handle) - - acquisition_controller.pre_acquire(self) + print("Capturing %d buffers." % buffers_per_acquisition) + +# acquisition_controller.pre_acquire(self) # buffer handling from acquisition buffers_completed = 0 + bytes_transferred = 0 buffer_timeout = self.buffer_timeout._get_byte() self.buffer_timeout._set_updated() buffer_recycling = (self.buffers_per_acquisition._get_byte() > self.allocated_buffers._get_byte()) - while buffers_completed < self.buffers_per_acquisition._get_byte(): + while (buffers_completed < self.buffers_per_acquisition._get_byte()): + # Wait for the buffer at the head of the list of available + # buffers to be filled by the board. buf = self.buffer_list[buffers_completed % allocated_buffers] self._call_dll('AlazarWaitAsyncBufferComplete', @@ -478,21 +526,21 @@ def acquire(self, mode=None, samples_per_record=None, # if buffers must be recycled, extract data and repost them # otherwise continue to next buffer - if buffer_recycling: - acquisition_controller.handle_buffer(self, buf.buffer) +# acquisition_controller.handle_buffer(self, buf.buffer) self._call_dll('AlazarPostAsyncBuffer', self._handle, buf.addr, buf.size_bytes) buffers_completed += 1 + bytes_transferred += buf.size_bytes # stop measurement here self._call_dll('AlazarAbortAsyncRead', self._handle) # -----cleanup here----- # extract data if not yet done - if not buffer_recycling: - for buf in self.buffer_list: - acquisition_controller.handle_buffer(self, buf.buffer) +# if not buffer_recycling: +# for buf in self.buffer_list: +# acquisition_controller.handle_buffer(self, buf.buffer) # free up memory self.clear_buffers() @@ -502,7 +550,24 @@ def acquire(self, mode=None, samples_per_record=None, p.get() # return result - return acquisition_controller.post_acquire(self) +# return acquisition_controller.post_acquire(self) + + # Compute the total transfer time, and display performance information. + transfer_time_sec = time.clock() - start + print("Capture completed in %f sec" % transfer_time_sec) + buffers_per_sec = 0 + bytes_per_sec = 0 + records_per_sec = 0 + if transfer_time_sec > 0: + buffers_per_sec = buffers_completed / transfer_time_sec + bytes_per_sec = bytes_transferred / transfer_time_sec + records_per_sec = records_per_buffer * buffers_completed / transfer_time_sec + print("Captured %d buffers (%f buffers per sec)" % + (buffers_completed, buffers_per_sec)) + print("Captured %d records (%f records per sec)" % + (records_per_buffer * buffers_completed, records_per_sec)) + print("Transferred %d bytes (%f bytes per sec)" % + (bytes_transferred, bytes_per_sec)) def _set_if_present(self, param_name, value): if value is not None: @@ -537,7 +602,12 @@ def _call_dll(self, func_name, *args): # run the function func = getattr(self._ATS_dll, func_name) - return_code = func(*args_out) + try: + return_code = func(*args_out) + print(return_code) + except Exception as e: + print(e) + raise # check for errors if (return_code != self._success) and (return_code !=518): @@ -563,6 +633,7 @@ def _call_dll(self, func_name, *args): def clear_buffers(self): for b in self.buffer_list: b.free_mem() + print("buffers cleared") self.buffer_list = [] def signal_to_volt(self, channel, signal): @@ -655,50 +726,75 @@ def _set_updated(self): self._uptodate_flag = True -class Buffer: - def __init__(self, bits_per_sample, samples_per_buffer, - number_of_channels): - if bits_per_sample != 8: - raise Exception("Buffer: only 8 bit per sample supported") - if os.name != 'nt': - raise Exception("Buffer: only Windows supported at this moment") - self._allocated = True - # try to allocate memory - mem_commit = 0x1000 - page_readwrite = 0x4 +class DMABuffer: + '''Buffer suitable for DMA transfers. + + AlazarTech digitizers use direct memory access (DMA) to transfer + data from digitizers to the computer's main memory. This class + abstracts a memory buffer on the host, and ensures that all the + requirements for DMA transfers are met. + + DMABuffers export a 'buffer' member, which is a NumPy array view + of the underlying memory buffer + + Args: + + c_sample_type (ctypes type): The datatype of the buffer to create. + + size_bytes (int): The size of the buffer to allocate, in bytes. - self.size_bytes = samples_per_buffer * number_of_channels + ''' + def __init__(self, c_sample_type, size_bytes): + self.size_bytes = size_bytes - # for documentation please see: - # https://msdn.microsoft.com/en-us/library/windows/desktop/aa366887(v=vs.85).aspx - ctypes.windll.kernel32.VirtualAlloc.argtypes = [ - ctypes.c_void_p, ctypes.c_long, ctypes.c_long, ctypes.c_long] - ctypes.windll.kernel32.VirtualAlloc.restype = ctypes.c_void_p - self.addr = ctypes.windll.kernel32.VirtualAlloc( - 0, ctypes.c_long(self.size_bytes), mem_commit, page_readwrite) - if self.addr is None: + npSampleType = { + ctypes.c_uint8: np.uint8, + ctypes.c_uint16: np.uint16, + ctypes.c_uint32: np.uint32, + ctypes.c_int32: np.int32, + ctypes.c_float: np.float32 + }.get(c_sample_type, 0) + + bytes_per_sample = { + ctypes.c_uint8: 1, + ctypes.c_uint16: 2, + ctypes.c_uint32: 4, + ctypes.c_int32: 4, + ctypes.c_float: 4 + }.get(c_sample_type, 0) + + self._allocated = True + self.addr = None + if os.name == 'nt': + MEM_COMMIT = 0x1000 + PAGE_READWRITE = 0x4 + ctypes.windll.kernel32.VirtualAlloc.argtypes = [ctypes.c_void_p, ctypes.c_long, ctypes.c_long, ctypes.c_long] + ctypes.windll.kernel32.VirtualAlloc.restype = ctypes.c_void_p + self.addr = ctypes.windll.kernel32.VirtualAlloc( + 0, ctypes.c_long(size_bytes), MEM_COMMIT, PAGE_READWRITE) + else: self._allocated = False - e = ctypes.windll.kernel32.GetLastError() - raise Exception("Memory allocation error: " + str(e)) + raise Exception("Unsupported OS") - ctypes_array = (ctypes.c_uint8 * - self.size_bytes).from_address(self.addr) - self.buffer = np.frombuffer(ctypes_array, dtype=np.uint8) + + ctypes_array = (c_sample_type * + (size_bytes // bytes_per_sample)).from_address(self.addr) + self.buffer = np.frombuffer(ctypes_array, dtype=npSampleType) + self.ctypes_buffer = ctypes_array pointer, read_only_flag = self.buffer.__array_interface__['data'] def free_mem(self): - mem_release = 0x8000 - - # for documentation please see: - # https://msdn.microsoft.com/en-us/library/windows/desktop/aa366892(v=vs.85).aspx - ctypes.windll.kernel32.VirtualFree.argtypes = [ - ctypes.c_void_p, ctypes.c_long, ctypes.c_long] - ctypes.windll.kernel32.VirtualFree.restype = ctypes.c_int - ctypes.windll.kernel32.VirtualFree(ctypes.c_void_p(self.addr), 0, - mem_release) self._allocated = False - + if os.name == 'nt': + MEM_RELEASE = 0x8000 + ctypes.windll.kernel32.VirtualFree.argtypes = [ctypes.c_void_p, ctypes.c_long, ctypes.c_long] + ctypes.windll.kernel32.VirtualFree.restype = ctypes.c_int + ctypes.windll.kernel32.VirtualFree(ctypes.c_void_p(self.addr), 0, MEM_RELEASE); + else: + self._allocated = True + raise Exception("Unsupported OS") + def __del__(self): if self._allocated: self.free_mem() @@ -706,7 +802,7 @@ def __del__(self): 'Buffer prevented memory leak; Memory released to Windows.\n' 'Memory should have been released before buffer was deleted.') - + class AcquisitionController: def __init__(self): """ diff --git a/qcodes/instrument_drivers/AlazarTech/ATS9360.py b/qcodes/instrument_drivers/AlazarTech/ATS9360.py new file mode 100644 index 000000000000..0db4a6c071d3 --- /dev/null +++ b/qcodes/instrument_drivers/AlazarTech/ATS9360.py @@ -0,0 +1,282 @@ +from .ATS import AlazarTech_ATS, AlazarParameter +from qcodes.utils import validators + + +class AlazarTech_ATS9360(AlazarTech_ATS): + def __init__(self, name, server_name=None): + dll_path = 'C:\\WINDOWS\\System32\\ATSApi.dll' + super().__init__(name, dll_path=dll_path) + + samplesPerSec = None + # add parameters + + # ----- Parameters for the configuration of the board ----- + self.add_parameter(name='clock_source', + parameter_class=AlazarParameter, + label='Clock Source', + unit=None, + value='INTERNAL_CLOCK', + byte_to_value_dict={1: 'INTERNAL_CLOCK', + 4: 'SLOW_EXTERNAL_CLOCK', + 5: 'EXTERNAL_CLOCK_AC', + 7: 'EXTERNAL_CLOCK_10_MHz_REF'}) + self.add_parameter(name='sample_rate', + parameter_class=AlazarParameter, + label='Sample Rate', + unit='S/s', + value=500000000, + byte_to_value_dict={ + 0x1: 1000, 0x2: 2000, 0x4: 5000, 0x8: 10000, + 0xA: 20000, 0xC: 50000, 0xE: 100000, + 0x10: 200000, 0x12: 500000, 0x14: 1000000, + 0x18: 2000000, 0x1A: 5000000, 0x1C: 10000000, + 0x1E: 20000000, 0x22: 50000000, 0x24: 100000000, + 0x2B: 250000000, 0x30: 500000000, + 0x35: 1000000000, 0x40: 'EXTERNAL_CLOCK', + 1000000000: '1GHz_REFERENCE_CLOCK'}) + self.add_parameter(name='clock_edge', + parameter_class=AlazarParameter, + label='Clock Edge', + unit=None, + value='CLOCK_EDGE_RISING', + byte_to_value_dict={0: 'CLOCK_EDGE_RISING', + 1: 'CLOCK_EDGE_FALLING'}) + + self.add_parameter(name='decimation', + parameter_class=AlazarParameter, + label='Decimation', + unit=None, + value=0, + vals=validators.Ints(0, 100000)) + + for i in ['1', '2']: + self.add_parameter(name='coupling' + i, + parameter_class=AlazarParameter, + label='Coupling channel ' + i, + unit=None, + value='DC', + byte_to_value_dict={1: 'AC', 2: 'DC'}) + self.add_parameter(name='channel_range' + i, + parameter_class=AlazarParameter, + label='Range channel ' + i, + unit='V', + value=0.4, + byte_to_value_dict={ + 2: 0.04, 5: 0.1, 6: 0.2, 7: 0.4, + 10: 1., 11: 2., 12: 4.}) + self.add_parameter(name='impedance' + i, + parameter_class=AlazarParameter, + label='Impedance channel ' + i, + unit='Ohm', + value=50, + byte_to_value_dict={1: 1000000, 2: 50}) + + self.add_parameter(name='trigger_operation', + parameter_class=AlazarParameter, + label='Trigger Operation', + unit=None, + value='TRIG_ENGINE_OP_J', + byte_to_value_dict={ + 0: 'TRIG_ENGINE_OP_J', + 1: 'TRIG_ENGINE_OP_K', + 2: 'TRIG_ENGINE_OP_J_OR_K', + 3: 'TRIG_ENGINE_OP_J_AND_K', + 4: 'TRIG_ENGINE_OP_J_XOR_K', + 5: 'TRIG_ENGINE_OP_J_AND_NOT_K', + 6: 'TRIG_ENGINE_OP_NOT_J_AND_K'}) + for i in ['1', '2']: + self.add_parameter(name='trigger_engine' + i, + parameter_class=AlazarParameter, + label='Trigger Engine ' + i, + unit=None, + value='TRIG_ENGINE_J', + byte_to_value_dict={0: 'TRIG_ENGINE_J', + 1: 'TRIG_ENGINE_K'}) + self.add_parameter(name='trigger_source' + i, + parameter_class=AlazarParameter, + label='Trigger Source ' + i, + unit=None, + value='DISABLE', + byte_to_value_dict={0: 'CHANNEL_A', + 1: 'CHANNEL_B', + 2: 'EXTERNAL', + 3: 'DISABLE'}) + self.add_parameter(name='trigger_slope' + i, + parameter_class=AlazarParameter, + label='Trigger Slope ' + i, + unit=None, + value='TRIG_SLOPE_POSITIVE', + byte_to_value_dict={1: 'TRIG_SLOPE_POSITIVE', + 2: 'TRIG_SLOPE_NEGATIVE'}) + self.add_parameter(name='trigger_level' + i, + parameter_class=AlazarParameter, + label='Trigger Level ' + i, + unit=None, + value=150, + vals=validators.Ints(0, 255)) + + self.add_parameter(name='external_trigger_coupling', + parameter_class=AlazarParameter, + label='External Trigger Coupling', + unit=None, + value='AC', + byte_to_value_dict={1: 'AC', 2: 'DC'}) + self.add_parameter(name='external_trigger_range', + parameter_class=AlazarParameter, + label='External Trigger Range', + unit=None, + value='ETR_5V', + byte_to_value_dict={0: 'ETR_5V', 1: 'ETR_1V'}) + self.add_parameter(name='trigger_delay', + parameter_class=AlazarParameter, + label='Trigger Delay', + unit='Sample clock cycles', + value=0, + vals=validators.Ints(min_value=0)) + + # NOTE: The board will wait for a for this amount of time for a + # trigger event. If a trigger event does not arrive, then the + # board will automatically trigger. Set the trigger timeout value + # to 0 to force the board to wait forever for a trigger event. + # + # IMPORTANT: The trigger timeout value should be set to zero after + # appropriate trigger parameters have been determined, otherwise + # the board may trigger if the timeout interval expires before a + # hardware trigger event arrives. + self.add_parameter(name='timeout_ticks', + parameter_class=AlazarParameter, + label='Timeout Ticks', + unit='10 us', + value=0, + vals=validators.Ints(min_value=0)) + + # TODO: need to confugure AuxIO?? + + # ----- Parameters for the acquire function ----- + self.add_parameter(name='mode', + parameter_class=AlazarParameter, + label='Acquisiton mode', + unit=None, + value='NPT', + byte_to_value_dict={0x200: 'NPT', 0x400: 'TS'}) + + # samples_per_record must be a multiple of 16! + self.add_parameter(name='samples_per_record', + parameter_class=AlazarParameter, + label='Samples per Record', + unit=None, + value=1024, + vals=Multiples(divisor=16, min_value=0)) + # TODO (M) figure out if this also has to be a multiple of something, + # I could not find this in the documentation but somehow I have the + # feeling it still should be a multiple of something + # NOTE by ramiro: At least in previous python implementations(PycQED delft), this is an artifact for compatibility with AWG sequencing, not particular to any ATS architecture. + # ==> this is a construction imposed by the memory strategy implemented on the python driver we are writing, not limited by any actual ATS feature. + + self.add_parameter(name='records_per_buffer', + parameter_class=AlazarParameter, + label='Records per Buffer', + unit=None, + value=10, + vals=validators.Ints(min_value=0)) + self.add_parameter(name='buffers_per_acquisition', + parameter_class=AlazarParameter, + label='Buffers per Acquisition', + unit=None, + value=10, + vals=validators.Ints(min_value=0)) + self.add_parameter(name='channel_selection', + parameter_class=AlazarParameter, + label='Channel Selection', + unit=None, + value='AB', + byte_to_value_dict={1: 'A', 2: 'B', 3: 'AB'}) + self.add_parameter(name='transfer_offset', + parameter_class=AlazarParameter, + label='Transer Offset', + unit='Samples', + value=0, + vals=validators.Ints(min_value=0)) + self.add_parameter(name='external_startcapture', + parameter_class=AlazarParameter, + label='External Startcapture', + unit=None, + value='ENABLED', + byte_to_value_dict={0x0: 'DISABLED', + 0x1: 'ENABLED'}) + self.add_parameter(name='enable_record_headers', + parameter_class=AlazarParameter, + label='Enable Record Headers', + unit=None, + value='DISABLED', + byte_to_value_dict={0x0: 'DISABLED', + 0x8: 'ENABLED'}) + self.add_parameter(name='alloc_buffers', + parameter_class=AlazarParameter, + label='Alloc Buffers', + unit=None, + value='DISABLED', + byte_to_value_dict={0x0: 'DISABLED', + 0x20: 'ENABLED'}) + self.add_parameter(name='fifo_only_streaming', + parameter_class=AlazarParameter, + label='Fifo Only Streaming', + unit=None, + value='DISABLED', + byte_to_value_dict={0x0: 'DISABLED', + 0x800: 'ENABLED'}) + self.add_parameter(name='interleave_samples', + parameter_class=AlazarParameter, + label='Interleave Samples', + unit=None, + value='DISABLED', + byte_to_value_dict={0x0: 'DISABLED', + 0x1000: 'ENABLED'}) + self.add_parameter(name='get_processed_data', + parameter_class=AlazarParameter, + label='Get Processed Data', + unit=None, + value='DISABLED', + byte_to_value_dict={0x0: 'DISABLED', + 0x2000: 'ENABLED'}) + + self.add_parameter(name='allocated_buffers', + parameter_class=AlazarParameter, + label='Allocated Buffers', + unit=None, + value=2, + vals=validators.Ints(min_value=0)) + self.add_parameter(name='buffer_timeout', + parameter_class=AlazarParameter, + label='Buffer Timeout', + unit='ms', + value=1000, + vals=validators.Ints(min_value=0)) + + # TODO (M) make parameter for board type + + # TODO (M) check board kind + + +class Multiples(validators.Ints): + ''' + requires an integer + optional parameters min_value and max_value enforce + min_value <= value <= max_value + divisor enforces that value % divisor == 0 + ''' + + def __init__(self, divisor=1, **kwargs): + super().__init__(**kwargs) + if not isinstance(divisor, int): + raise TypeError('divisor must be an integer') + self._divisor = divisor + + def validate(self, value, context=''): + super().validate(value=value, context=context) + if not value % self._divisor == 0: + raise TypeError('{} is not a multiple of {}; {}'.format( + repr(value), repr(self._divisor), context)) + + def __repr__(self): + return super().__repr__()[:-1] + ', Multiples of {}>'.format(self._divisor) diff --git a/qcodes/instrument_drivers/AlazarTech/ATS_acquisition_controllers.py b/qcodes/instrument_drivers/AlazarTech/ATS_acquisition_controllers.py index 4122d9da583d..0614cb66949b 100644 --- a/qcodes/instrument_drivers/AlazarTech/ATS_acquisition_controllers.py +++ b/qcodes/instrument_drivers/AlazarTech/ATS_acquisition_controllers.py @@ -6,17 +6,24 @@ # DFT AcquisitionController class DFT_AcquisitionController(AcquisitionController): def __init__(self, demodulation_frequency): - self.demodulation_frequency = demodulation_frequency + # self.demodulation_frequency = demodulation_frequency self.samples_per_record = None + self.bits_per_sample = None self.records_per_buffer = None self.buffers_per_acquisition = None + self.allocated_buffers = None # TODO (S) this is not very general: self.number_of_channels = 2 - self.cos_list = None - self.sin_list = None + # self.cos_list = None + # self.sin_list = None self.buffer = None def pre_start_capture(self, alazar): + + # allcoate buffers + max_s, bps = alazar._get_channel_info() + self.bits_per_sample = bps + self.samples_per_record = alazar.samples_per_record.get() self.records_per_buffer = alazar.records_per_buffer.get() self.buffers_per_acquisition = alazar.buffers_per_acquisition.get() From 256b35a4fcbeae936ec4cd7766c4c1c082b025e0 Mon Sep 17 00:00:00 2001 From: Natalie Pearson Date: Tue, 2 Aug 2016 19:18:45 +0200 Subject: [PATCH 02/17] style: Remove some print statememnts from ATS.py --- qcodes/instrument_drivers/AlazarTech/ATS.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/qcodes/instrument_drivers/AlazarTech/ATS.py b/qcodes/instrument_drivers/AlazarTech/ATS.py index 463664163c94..eba15498d61d 100644 --- a/qcodes/instrument_drivers/AlazarTech/ATS.py +++ b/qcodes/instrument_drivers/AlazarTech/ATS.py @@ -18,11 +18,13 @@ # acquisition that would overflow the board if measurement is not stopped # quickly enough. can this be solved by not reposting the buffers? -# TODO: check 8 bit sample thing works -# TODO: test get_board_info and find_boards -# TODO: remove guerilla debuggung 'print' statements -# TODO: call_dll error handling -# TODO: acquisition_controller +# TODO: check 8 bit sample thing works - Natalie +# TODO: test get_board_info and find_boards - Natalie +# TODO: remove guerilla debuggung 'print' statements - Natalie +# TODO: call_dll error handling - Natalie +# TODO: acquisition_controller - Natalie +# TODO: fix hacky way I use _ATS_dll anywhere - Natalie +# TODO: make it actually save data! - Natalie class AlazarTech_ATS(Instrument): # override dll_path in your init script or in the board constructor @@ -476,7 +478,7 @@ def acquire(self, mode=None, samples_per_record=None, self.clear_buffers() allocated_buffers = self.allocated_buffers._get_byte() - print(str(allocated_buffers)+"allocated buffers") + print(str(allocated_buffers)+" allocated buffers") for k in range(allocated_buffers): try: self.buffer_list.append(DMABuffer(sample_type, bytes_per_buffer)) @@ -487,8 +489,6 @@ def acquire(self, mode=None, samples_per_record=None, # post buffers to Alazar print("made buffer list length "+str(len(self.buffer_list))) for buf in self.buffer_list: - print(buf.addr) - print(type(buf.size_bytes)) self._ATS_dll.AlazarPostAsyncBuffer.argtypes = [ctypes.c_uint32, ctypes.c_void_p, ctypes.c_uint32] self._call_dll('AlazarPostAsyncBuffer', self._handle, buf.addr, buf.size_bytes) @@ -604,7 +604,6 @@ def _call_dll(self, func_name, *args): func = getattr(self._ATS_dll, func_name) try: return_code = func(*args_out) - print(return_code) except Exception as e: print(e) raise From 9b0d84bd15c625497d8fed5300fce127becba4be Mon Sep 17 00:00:00 2001 From: Natalie Pearson Date: Wed, 3 Aug 2016 18:09:49 +0200 Subject: [PATCH 03/17] tidy up ATS.py comments and create test acquisition conroller for ATS9360 --- docs/examples/Qcodes example ATS_ONWORK.ipynb | 1742 +++++++++++++++-- qcodes/instrument_drivers/AlazarTech/ATS.py | 37 +- .../instrument_drivers/AlazarTech/ATS9360.py | 1 - .../AlazarTech/ATS_acquisition_controllers.py | 112 +- 4 files changed, 1736 insertions(+), 156 deletions(-) diff --git a/docs/examples/Qcodes example ATS_ONWORK.ipynb b/docs/examples/Qcodes example ATS_ONWORK.ipynb index 206812252a0a..54114498186f 100644 --- a/docs/examples/Qcodes example ATS_ONWORK.ipynb +++ b/docs/examples/Qcodes example ATS_ONWORK.ipynb @@ -383,21 +383,10 @@ "metadata": { "collapsed": false }, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "import qcodes.instrument_drivers.AlazarTech.ATS9360 as ATSdriver; reload(ATSdriver)\n", - "#import qcodes.instrument_drivers.AlazarTech.ATS_acquisition_controllers as ats_contr" + "import qcodes.instrument_drivers.AlazarTech.ATS_acquisition_controllers as ats_contr" ] }, { @@ -408,7 +397,7 @@ }, "outputs": [], "source": [ - "#contr1 = ats_contr.DFT_AcquisitionController(demodulation_frequency=10e6)" + "contr1 = ats_contr.Test_AcquisitionController()" ] }, { @@ -428,35 +417,7 @@ "metadata": { "collapsed": false }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "AlazarSetCaptureClock\n", - "512\n", - "\n", - "AlazarInputControl\n", - "512\n", - "\n", - "AlazarInputControl\n", - "512\n", - "\n", - "AlazarSetTriggerOperation\n", - "512\n", - "\n", - "AlazarSetExternalTrigger\n", - "512\n", - "\n", - "AlazarSetTriggerDelay\n", - "512\n", - "\n", - "AlazarSetTriggerTimeOut\n", - "512\n" - ] - } - ], + "outputs": [], "source": [ "ats_inst.config(clock_source='INTERNAL_CLOCK',\n", " sample_rate=500000000,\n", @@ -491,91 +452,37 @@ "name": "stdout", "output_type": "stream", "text": [ - "\n", - "AlazarAbortAsyncRead\n", - "512\n", - "\n", - "AlazarSetRecordSize\n", - "512\n", - "\n", - "AlazarBeforeAsyncRead\n", - "512\n", - "\n", - "AlazarGetChannelInfo\n", - "512\n", "bytes per sample 2\n", "samples_per_record is 1024\n", "bytes per record is 2048\n", "samples_per_buffer is 10240\n", "bytes_per_buffer is 40960\n", + "records_per_buffer is 10\n", "buffers cleared\n", - "2allocated buffers\n", - "making DMA buffer\n", - "making DMA buffer\n", "made buffer list length 2\n", - "138543104\n", - "\n", - "\n", - "AlazarPostAsyncBuffer\n", - "512\n", - "138608640\n", - "\n", - "\n", - "AlazarPostAsyncBuffer\n", - "512\n", "completed AlazarPostAsyncBuffer\n", - "\n", - "AlazarStartCapture\n", - "512\n", - "Capturing 3 buffers.\n", - "\n", - "AlazarWaitAsyncBufferComplete\n", - "518\n", - "[31136 36320 31360 ..., 28736 32480 28832]\n", - "\n", - "AlazarPostAsyncBuffer\n", - "518\n", - "\n", - "AlazarWaitAsyncBufferComplete\n", - "518\n", - "[31136 36320 31360 ..., 28736 32480 28832]\n", - "\n", - "AlazarPostAsyncBuffer\n", - "518\n", - "\n", - "AlazarWaitAsyncBufferComplete\n", - "518\n", - "[31136 36320 31360 ..., 28736 32480 28832]\n", - "\n", - "AlazarPostAsyncBuffer\n", - "518\n", - "\n", - "AlazarAbortAsyncRead\n", - "512\n", - "buffers cleared\n", - "Capture completed in 0.026781 sec\n", - "Captured 3 buffers (112.018308 buffers per sec)\n", - "Captured 30 records (1120.183076 records per sec)\n", - "Transferred 122880 bytes (4588269.880869 bytes per sec)\n" + "Capturing 1 buffers.\n", + "not recycling\n", + "buffers cleared\n" ] } ], "source": [ - "ats_inst.acquire(mode='NPT',\n", - " samples_per_record=1024,\n", - " records_per_buffer=10,\n", - " buffers_per_acquisition=3,\n", - " channel_selection='AB',\n", - " transfer_offset=0,\n", - " external_startcapture='DISABLED',\n", - " enable_record_headers='DISABLED',\n", - " alloc_buffers='ENABLED',\n", - " fifo_only_streaming='DISABLED',\n", - " interleave_samples='DISABLED',\n", - " get_processed_data='ENABLED',\n", - " allocated_buffers=2,\n", - " buffer_timeout=10,\n", - " acquisition_controller=None)" + "resA, resB = ats_inst.acquire(mode='NPT',\n", + " samples_per_record=1024,\n", + " records_per_buffer=10,\n", + " buffers_per_acquisition=1,\n", + " channel_selection='AB',\n", + " transfer_offset=0,\n", + " external_startcapture='DISABLED',\n", + " enable_record_headers='DISABLED',\n", + " alloc_buffers='ENABLED',\n", + " fifo_only_streaming='DISABLED',\n", + " interleave_samples='DISABLED',\n", + " get_processed_data='ENABLED',\n", + " allocated_buffers=2,\n", + " buffer_timeout=10,\n", + " acquisition_controller=contr1)" ] }, { @@ -599,31 +506,1610 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 8, "metadata": { "collapsed": false }, "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "AlazarGetChannelInfo\n", - "512\n" - ] + "data": { + "text/plain": [ + "12" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ats_inst.get_idn()['bits_per_sample']" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "500000000" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ats_inst.get_sample_speed()" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "application/javascript": [ + "/* Put everything inside the global mpl namespace */\n", + "window.mpl = {};\n", + "\n", + "mpl.get_websocket_type = function() {\n", + " if (typeof(WebSocket) !== 'undefined') {\n", + " return WebSocket;\n", + " } else if (typeof(MozWebSocket) !== 'undefined') {\n", + " return MozWebSocket;\n", + " } else {\n", + " alert('Your browser does not have WebSocket support.' +\n", + " 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", + " 'Firefox 4 and 5 are also supported but you ' +\n", + " 'have to enable WebSockets in about:config.');\n", + " };\n", + "}\n", + "\n", + "mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n", + " this.id = figure_id;\n", + "\n", + " this.ws = websocket;\n", + "\n", + " this.supports_binary = (this.ws.binaryType != undefined);\n", + "\n", + " if (!this.supports_binary) {\n", + " var warnings = document.getElementById(\"mpl-warnings\");\n", + " if (warnings) {\n", + " warnings.style.display = 'block';\n", + " warnings.textContent = (\n", + " \"This browser does not support binary websocket messages. \" +\n", + " \"Performance may be slow.\");\n", + " }\n", + " }\n", + "\n", + " this.imageObj = new Image();\n", + "\n", + " this.context = undefined;\n", + " this.message = undefined;\n", + " this.canvas = undefined;\n", + " this.rubberband_canvas = undefined;\n", + " this.rubberband_context = undefined;\n", + " this.format_dropdown = undefined;\n", + "\n", + " this.image_mode = 'full';\n", + "\n", + " this.root = $('
');\n", + " this._root_extra_style(this.root)\n", + " this.root.attr('style', 'display: inline-block');\n", + "\n", + " $(parent_element).append(this.root);\n", + "\n", + " this._init_header(this);\n", + " this._init_canvas(this);\n", + " this._init_toolbar(this);\n", + "\n", + " var fig = this;\n", + "\n", + " this.waiting = false;\n", + "\n", + " this.ws.onopen = function () {\n", + " fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n", + " fig.send_message(\"send_image_mode\", {});\n", + " fig.send_message(\"refresh\", {});\n", + " }\n", + "\n", + " this.imageObj.onload = function() {\n", + " if (fig.image_mode == 'full') {\n", + " // Full images could contain transparency (where diff images\n", + " // almost always do), so we need to clear the canvas so that\n", + " // there is no ghosting.\n", + " fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", + " }\n", + " fig.context.drawImage(fig.imageObj, 0, 0);\n", + " fig.waiting = false;\n", + " };\n", + "\n", + " this.imageObj.onunload = function() {\n", + " this.ws.close();\n", + " }\n", + "\n", + " this.ws.onmessage = this._make_on_message_function(this);\n", + "\n", + " this.ondownload = ondownload;\n", + "}\n", + "\n", + "mpl.figure.prototype._init_header = function() {\n", + " var titlebar = $(\n", + " '
');\n", + " var titletext = $(\n", + " '
');\n", + " titlebar.append(titletext)\n", + " this.root.append(titlebar);\n", + " this.header = titletext[0];\n", + "}\n", + "\n", + "\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n", + "\n", + "}\n", + "\n", + "\n", + "mpl.figure.prototype._root_extra_style = function(canvas_div) {\n", + "\n", + "}\n", + "\n", + "mpl.figure.prototype._init_canvas = function() {\n", + " var fig = this;\n", + "\n", + " var canvas_div = $('
');\n", + "\n", + " canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n", + "\n", + " function canvas_keyboard_event(event) {\n", + " return fig.key_event(event, event['data']);\n", + " }\n", + "\n", + " canvas_div.keydown('key_press', canvas_keyboard_event);\n", + " canvas_div.keyup('key_release', canvas_keyboard_event);\n", + " this.canvas_div = canvas_div\n", + " this._canvas_extra_style(canvas_div)\n", + " this.root.append(canvas_div);\n", + "\n", + " var canvas = $('');\n", + " canvas.addClass('mpl-canvas');\n", + " canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n", + "\n", + " this.canvas = canvas[0];\n", + " this.context = canvas[0].getContext(\"2d\");\n", + "\n", + " var rubberband = $('');\n", + " rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n", + "\n", + " var pass_mouse_events = true;\n", + "\n", + " canvas_div.resizable({\n", + " start: function(event, ui) {\n", + " pass_mouse_events = false;\n", + " },\n", + " resize: function(event, ui) {\n", + " fig.request_resize(ui.size.width, ui.size.height);\n", + " },\n", + " stop: function(event, ui) {\n", + " pass_mouse_events = true;\n", + " fig.request_resize(ui.size.width, ui.size.height);\n", + " },\n", + " });\n", + "\n", + " function mouse_event_fn(event) {\n", + " if (pass_mouse_events)\n", + " return fig.mouse_event(event, event['data']);\n", + " }\n", + "\n", + " rubberband.mousedown('button_press', mouse_event_fn);\n", + " rubberband.mouseup('button_release', mouse_event_fn);\n", + " // Throttle sequential mouse events to 1 every 20ms.\n", + " rubberband.mousemove('motion_notify', mouse_event_fn);\n", + "\n", + " rubberband.mouseenter('figure_enter', mouse_event_fn);\n", + " rubberband.mouseleave('figure_leave', mouse_event_fn);\n", + "\n", + " canvas_div.on(\"wheel\", function (event) {\n", + " event = event.originalEvent;\n", + " event['data'] = 'scroll'\n", + " if (event.deltaY < 0) {\n", + " event.step = 1;\n", + " } else {\n", + " event.step = -1;\n", + " }\n", + " mouse_event_fn(event);\n", + " });\n", + "\n", + " canvas_div.append(canvas);\n", + " canvas_div.append(rubberband);\n", + "\n", + " this.rubberband = rubberband;\n", + " this.rubberband_canvas = rubberband[0];\n", + " this.rubberband_context = rubberband[0].getContext(\"2d\");\n", + " this.rubberband_context.strokeStyle = \"#000000\";\n", + "\n", + " this._resize_canvas = function(width, height) {\n", + " // Keep the size of the canvas, canvas container, and rubber band\n", + " // canvas in synch.\n", + " canvas_div.css('width', width)\n", + " canvas_div.css('height', height)\n", + "\n", + " canvas.attr('width', width);\n", + " canvas.attr('height', height);\n", + "\n", + " rubberband.attr('width', width);\n", + " rubberband.attr('height', height);\n", + " }\n", + "\n", + " // Set the figure to an initial 600x600px, this will subsequently be updated\n", + " // upon first draw.\n", + " this._resize_canvas(600, 600);\n", + "\n", + " // Disable right mouse context menu.\n", + " $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n", + " return false;\n", + " });\n", + "\n", + " function set_focus () {\n", + " canvas.focus();\n", + " canvas_div.focus();\n", + " }\n", + "\n", + " window.setTimeout(set_focus, 100);\n", + "}\n", + "\n", + "mpl.figure.prototype._init_toolbar = function() {\n", + " var fig = this;\n", + "\n", + " var nav_element = $('
')\n", + " nav_element.attr('style', 'width: 100%');\n", + " this.root.append(nav_element);\n", + "\n", + " // Define a callback function for later on.\n", + " function toolbar_event(event) {\n", + " return fig.toolbar_button_onclick(event['data']);\n", + " }\n", + " function toolbar_mouse_event(event) {\n", + " return fig.toolbar_button_onmouseover(event['data']);\n", + " }\n", + "\n", + " for(var toolbar_ind in mpl.toolbar_items) {\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) {\n", + " // put a spacer in here.\n", + " continue;\n", + " }\n", + " var button = $('');\n", + " button.click(method_name, toolbar_event);\n", + " button.mouseover(tooltip, toolbar_mouse_event);\n", + " nav_element.append(button);\n", + " }\n", + "\n", + " // Add the status bar.\n", + " var status_bar = $('');\n", + " nav_element.append(status_bar);\n", + " this.message = status_bar[0];\n", + "\n", + " // Add the close button to the window.\n", + " var buttongrp = $('
');\n", + " var button = $('');\n", + " button.click(function (evt) { fig.handle_close(fig, {}); } );\n", + " button.mouseover('Close figure', toolbar_mouse_event);\n", + " buttongrp.append(button);\n", + " var titlebar = this.root.find($('.ui-dialog-titlebar'));\n", + " titlebar.prepend(buttongrp);\n", + "}\n", + "\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function(el){\n", + " // this is important to make the div 'focusable\n", + " el.attr('tabindex', 0)\n", + " // reach out to IPython and tell the keyboard manager to turn it's self\n", + " // off when our div gets focus\n", + "\n", + " // location in version 3\n", + " if (IPython.notebook.keyboard_manager) {\n", + " IPython.notebook.keyboard_manager.register_events(el);\n", + " }\n", + " else {\n", + " // location in version 2\n", + " IPython.keyboard_manager.register_events(el);\n", + " }\n", + "\n", + "}\n", + "\n", + "mpl.figure.prototype._key_event_extra = function(event, name) {\n", + " var manager = IPython.notebook.keyboard_manager;\n", + " if (!manager)\n", + " manager = IPython.keyboard_manager;\n", + "\n", + " // Check for shift+enter\n", + " if (event.shiftKey && event.which == 13) {\n", + " this.canvas_div.blur();\n", + " event.shiftKey = false;\n", + " // Send a \"J\" for go to next cell\n", + " event.which = 74;\n", + " event.keyCode = 74;\n", + " manager.command_mode();\n", + " manager.handle_keydown(event);\n", + " }\n", + "}\n", + "\n", + "mpl.figure.prototype.handle_save = function(fig, msg) {\n", + " fig.ondownload(fig, null);\n", + "}\n", + "\n", + "\n", + "mpl.find_output_cell = function(html_output) {\n", + " // Return the cell and output element which can be found *uniquely* in the notebook.\n", + " // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", + " // IPython event is triggered only after the cells have been serialised, which for\n", + " // our purposes (turning an active figure into a static one), is too late.\n", + " var cells = IPython.notebook.get_cells();\n", + " var ncells = cells.length;\n", + " for (var i=0; i= 3 moved mimebundle to data attribute of output\n", + " data = data.data;\n", + " }\n", + " if (data['text/html'] == html_output) {\n", + " return [cell, data, j];\n", + " }\n", + " }\n", + " }\n", + " }\n", + "}\n", + "\n", + "// Register the function which deals with the matplotlib target/channel.\n", + "// The kernel may be null if the page has been refreshed.\n", + "if (IPython.notebook.kernel != null) {\n", + " IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n", + "}\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "qc.MatPlot(resB)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "ename": "TypeError", + "evalue": "__init__() got an unexpected keyword argument 'system_id'", + "output_type": "error", + "traceback": [ + "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[1;31mTypeError\u001b[0m Traceback (most recent call last)", + "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m()\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0mats_inst\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mget_idn\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[1;32ma:\\qcodesfolder\\qcodes\\instrument_drivers\\AlazarTech\\ATS.py\u001b[0m in \u001b[0;36mget_idn\u001b[1;34m(self)\u001b[0m\n\u001b[0;32m 216\u001b[0m \u001b[0mboard_kind\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_board_names\u001b[0m\u001b[1;33m[\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_ATS_dll\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mAlazarGetBoardKind\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_handle\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 217\u001b[0m \u001b[0mmax_s\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mbps\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_get_channel_info\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_handle\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 218\u001b[1;33m \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mfind_boards\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 219\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 220\u001b[0m return {'board' : board_kind,\n", + "\u001b[1;32ma:\\qcodesfolder\\qcodes\\instrument_drivers\\AlazarTech\\ATS.py\u001b[0m in \u001b[0;36mfind_boards\u001b[1;34m(cls, dll_path)\u001b[0m\n\u001b[0;32m 170\u001b[0m \u001b[0mboard_count\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mdll\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mAlazarBoardsInSystemBySystemID\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0msystem_id\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 171\u001b[0m \u001b[1;32mfor\u001b[0m \u001b[0mboard_id\u001b[0m \u001b[1;32min\u001b[0m \u001b[0mrange\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m1\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mboard_count\u001b[0m \u001b[1;33m+\u001b[0m \u001b[1;36m1\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 172\u001b[1;33m \u001b[0mboards\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mappend\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mcls\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mget_board_info\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mdll\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0msystem_id\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mboard_id\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 173\u001b[0m \u001b[1;32mreturn\u001b[0m \u001b[0mboards\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 174\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n", + "\u001b[1;32ma:\\qcodesfolder\\qcodes\\instrument_drivers\\AlazarTech\\ATS.py\u001b[0m in \u001b[0;36mget_board_info\u001b[1;34m(cls, dll, system_id, board_id)\u001b[0m\n\u001b[0;32m 178\u001b[0m \u001b[1;31m# to get its info\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 179\u001b[0m board = cls('temp', system_id=system_id, board_id=board_id,\n\u001b[1;32m--> 180\u001b[1;33m server_name=None)\n\u001b[0m\u001b[0;32m 181\u001b[0m \u001b[0mhandle\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mboard\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_handle\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 182\u001b[0m \u001b[0mboard_kind\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mcls\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_board_names\u001b[0m\u001b[1;33m[\u001b[0m\u001b[0mdll\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mAlazarGetBoardKind\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mhandle\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", + "\u001b[1;31mTypeError\u001b[0m: __init__() got an unexpected keyword argument 'system_id'" + ] + } + ], "source": [ "ats_inst.get_idn()" ] @@ -635,7 +2121,9 @@ "collapsed": true }, "outputs": [], - "source": [] + "source": [ + "import qcodes.instrument_drivers.AlazarTech.ATS" + ] } ], "metadata": { diff --git a/qcodes/instrument_drivers/AlazarTech/ATS.py b/qcodes/instrument_drivers/AlazarTech/ATS.py index eba15498d61d..084ca36d14e9 100644 --- a/qcodes/instrument_drivers/AlazarTech/ATS.py +++ b/qcodes/instrument_drivers/AlazarTech/ATS.py @@ -18,13 +18,9 @@ # acquisition that would overflow the board if measurement is not stopped # quickly enough. can this be solved by not reposting the buffers? -# TODO: check 8 bit sample thing works - Natalie -# TODO: test get_board_info and find_boards - Natalie -# TODO: remove guerilla debuggung 'print' statements - Natalie -# TODO: call_dll error handling - Natalie -# TODO: acquisition_controller - Natalie -# TODO: fix hacky way I use _ATS_dll anywhere - Natalie -# TODO: make it actually save data! - Natalie +# TODO(nataliejpg) get_board_info and find_boards are broken?? +# TODO(nataliejpg) call_dll error handling doesnt catch errors in expected way +# TODO(nataliejpg) make use of _ATS_dll uniform class AlazarTech_ATS(Instrument): # override dll_path in your init script or in the board constructor @@ -214,7 +210,6 @@ def __init__(self, name, system_id=1, board_id=1, dll_path=None, **kwargs): self.buffer_list = [] - # TODO use get_board_info for this and make it return better stuff! def get_idn(self): board_kind = self._board_names[self._ATS_dll.AlazarGetBoardKind(self._handle)] max_s, bps = self._get_channel_info(self._handle) @@ -459,26 +454,27 @@ def acquire(self, mode=None, samples_per_record=None, print("samples_per_record is "+str(samples_per_record)) print("bytes per record is "+str(bytes_per_record)) - # bytes per buffer + # channels if self.channel_selection._get_byte() == 3: number_of_channels = 2 else: number_of_channels = 1 - + + # bytes per buffer bytes_per_buffer = bytes_per_record * records_per_buffer * number_of_channels # create buffers for acquisition - # TODO: should this be > 1 (makes sense to me) or > 8 as in alazar sample code? - Natalie + # TODO(nataliejpg) should this be > 1 (makes sense to me) or > 8 as in alazar sample code? - Natalie sample_type = ctypes.c_uint8 if bytes_per_sample > 1: sample_type = ctypes.c_uint16 print("samples_per_buffer is "+str(samples_per_buffer)) print("bytes_per_buffer is "+str(bytes_per_buffer)) + print("records_per_buffer is "+str(records_per_buffer)) self.clear_buffers() allocated_buffers = self.allocated_buffers._get_byte() - print(str(allocated_buffers)+" allocated buffers") for k in range(allocated_buffers): try: self.buffer_list.append(DMABuffer(sample_type, bytes_per_buffer)) @@ -496,13 +492,13 @@ def acquire(self, mode=None, samples_per_record=None, print("completed AlazarPostAsyncBuffer") # -----start capture here----- -# acquisition_controller.pre_start_capture(self) + acquisition_controller.pre_start_capture(self) start = time.clock() # Keep track of when acquisition started # call the startcapture method self._call_dll('AlazarStartCapture', self._handle) print("Capturing %d buffers." % buffers_per_acquisition) -# acquisition_controller.pre_acquire(self) + acquisition_controller.pre_acquire(self) # buffer handling from acquisition buffers_completed = 0 bytes_transferred = 0 @@ -527,7 +523,7 @@ def acquire(self, mode=None, samples_per_record=None, # if buffers must be recycled, extract data and repost them # otherwise continue to next buffer if buffer_recycling: -# acquisition_controller.handle_buffer(self, buf.buffer) + acquisition_controller.handle_buffer(self, buf.buffer) self._call_dll('AlazarPostAsyncBuffer', self._handle, buf.addr, buf.size_bytes) buffers_completed += 1 @@ -538,9 +534,9 @@ def acquire(self, mode=None, samples_per_record=None, # -----cleanup here----- # extract data if not yet done -# if not buffer_recycling: -# for buf in self.buffer_list: -# acquisition_controller.handle_buffer(self, buf.buffer) + if not buffer_recycling: + for buf in self.buffer_list: + acquisition_controller.handle_buffer(self, buf.buffer) # free up memory self.clear_buffers() @@ -550,7 +546,7 @@ def acquire(self, mode=None, samples_per_record=None, p.get() # return result -# return acquisition_controller.post_acquire(self) + return acquisition_controller.post_acquire(self) # Compute the total transfer time, and display performance information. transfer_time_sec = time.clock() - start @@ -640,7 +636,8 @@ def signal_to_volt(self, channel, signal): # TODO (M) use byte value if range{channel} return (((signal - 127.5) / 127.5) * (self.parameters['channel_range' + str(channel)].get())) - + + # TODO(nataliejpg) surely this should be get_sample_rate? def get_sample_speed(self): if self.sample_rate.get() == 'EXTERNAL_CLOCK': raise Exception('External clock is used, alazar driver ' diff --git a/qcodes/instrument_drivers/AlazarTech/ATS9360.py b/qcodes/instrument_drivers/AlazarTech/ATS9360.py index 0db4a6c071d3..5703d92a6bd5 100644 --- a/qcodes/instrument_drivers/AlazarTech/ATS9360.py +++ b/qcodes/instrument_drivers/AlazarTech/ATS9360.py @@ -150,7 +150,6 @@ def __init__(self, name, server_name=None): value=0, vals=validators.Ints(min_value=0)) - # TODO: need to confugure AuxIO?? # ----- Parameters for the acquire function ----- self.add_parameter(name='mode', diff --git a/qcodes/instrument_drivers/AlazarTech/ATS_acquisition_controllers.py b/qcodes/instrument_drivers/AlazarTech/ATS_acquisition_controllers.py index 0614cb66949b..359a767624b0 100644 --- a/qcodes/instrument_drivers/AlazarTech/ATS_acquisition_controllers.py +++ b/qcodes/instrument_drivers/AlazarTech/ATS_acquisition_controllers.py @@ -3,10 +3,111 @@ import numpy as np +# Heterodyne Measurement Controller +# returns unprocessed data averaged by record for 2 channel use +class HD_Controller(AcquisitionController): + def __init__(self): + self.samples_per_record = None + self.bits_per_sample = None + self.records_per_buffer = None + self.buffers_per_acquisition = None + self.allocated_buffers = None + self.number_of_channels = 2 + self.buffer = None + + def pre_start_capture(self, alazar): + self.bits_per_sample = alazar.get_idn()['bits_per_sample'] + self.samples_per_record = alazar.samples_per_record() + self.records_per_buffer = alazar.records_per_buffer() + self.buffers_per_acquisition = alazar.buffers_per_acquisition() + sample_speed = alazar.get_sample_speed() + self.buffer = np.zeros(self.samples_per_record * + self.records_per_buffer * + self.number_of_channels) + + def pre_acquire(self, alazar): + # gets called after 'AlazarStartCapture' + pass + + def handle_buffer(self, alazar, data): + self.buffer += data + + def post_acquire(self, alazar): + # average over records in buffer: + # for ATS9360 samples are arranged in the buffer as follows: + # S0A, S0B, ..., S1A, S1B, ... + # with SXY the sample number X of channel Y. + records_per_acquisition = (1. * self.buffers_per_acquisition * + self.records_per_buffer) + recordA = np.zeros(self.samples_per_record) + for i in range(self.records_per_buffer): + i0 = i * self.samples_per_record * self.number_of_channels + i1 = i0 + self.samples_per_record * self.number_of_channels + recordA += self.buffer[i0:i1:self.number_of_channels] / records_per_acquisition + + recordB = np.zeros(self.samples_per_record) + for i in range(self.records_per_buffer): + i0 = (i * self.samples_per_record * self.number_of_channels) + 1 + i1 = i0 + self.samples_per_record *self.number_of_channels + recordB += self.buffer[i0:i1:self.number_of_channels] / records_per_acquisition + return recordA, recordB + + +# Test AcquisitionController tested on ATS9360 (nataliejpg) +# returns unprocessed data averaged by record for 2 channel use +class Test_AcquisitionController(AcquisitionController): + def __init__(self): + self.samples_per_record = None + self.bits_per_sample = None + self.records_per_buffer = None + self.buffers_per_acquisition = None + self.allocated_buffers = None + self.number_of_channels = 2 + self.buffer = None + + def pre_start_capture(self, alazar): + self.bits_per_sample = alazar.get_idn()['bits_per_sample'] + self.samples_per_record = alazar.samples_per_record() + self.records_per_buffer = alazar.records_per_buffer() + self.buffers_per_acquisition = alazar.buffers_per_acquisition() + sample_speed = alazar.get_sample_speed() + self.buffer = np.zeros(self.samples_per_record * + self.records_per_buffer * + self.number_of_channels) + + def pre_acquire(self, alazar): + # gets called after 'AlazarStartCapture' + pass + + def handle_buffer(self, alazar, data): + self.buffer += data + + def post_acquire(self, alazar): + # average over records in buffer: + # for ATS9360 samples are arranged in the buffer as follows: + # S0A, S0B, ..., S1A, S1B, ... + # with SXY the sample number X of channel Y. + records_per_acquisition = (1. * self.buffers_per_acquisition * + self.records_per_buffer) + recordA = np.zeros(self.samples_per_record) + for i in range(self.records_per_buffer): + i0 = i * self.samples_per_record * self.number_of_channels + i1 = i0 + self.samples_per_record * self.number_of_channels + recordA += self.buffer[i0:i1:self.number_of_channels] / records_per_acquisition + + recordB = np.zeros(self.samples_per_record) + for i in range(self.records_per_buffer): + i0 = (i * self.samples_per_record * self.number_of_channels) + 1 + i1 = i0 + self.samples_per_record *self.number_of_channels + recordB += self.buffer[i0:i1:self.number_of_channels] / records_per_acquisition + return recordA, recordB + + + # DFT AcquisitionController class DFT_AcquisitionController(AcquisitionController): def __init__(self, demodulation_frequency): - # self.demodulation_frequency = demodulation_frequency + self.demodulation_frequency = demodulation_frequency self.samples_per_record = None self.bits_per_sample = None self.records_per_buffer = None @@ -14,16 +115,11 @@ def __init__(self, demodulation_frequency): self.allocated_buffers = None # TODO (S) this is not very general: self.number_of_channels = 2 - # self.cos_list = None - # self.sin_list = None + self.cos_list = None + self.sin_list = None self.buffer = None def pre_start_capture(self, alazar): - - # allcoate buffers - max_s, bps = alazar._get_channel_info() - self.bits_per_sample = bps - self.samples_per_record = alazar.samples_per_record.get() self.records_per_buffer = alazar.records_per_buffer.get() self.buffers_per_acquisition = alazar.buffers_per_acquisition.get() From 5f02af04b05b62b41b15fe7648d1b1434b7a3aa1 Mon Sep 17 00:00:00 2001 From: Natalie Pearson Date: Thu, 4 Aug 2016 15:23:53 +0200 Subject: [PATCH 04/17] more edits on acquisition_controller --- docs/examples/Qcodes example ATS_ONWORK.ipynb | 83 +++++++++++++------ qcodes/instrument_drivers/AlazarTech/ATS.py | 1 + .../AlazarTech/ATS_acquisition_controllers.py | 34 ++++++-- 3 files changed, 85 insertions(+), 33 deletions(-) diff --git a/docs/examples/Qcodes example ATS_ONWORK.ipynb b/docs/examples/Qcodes example ATS_ONWORK.ipynb index 54114498186f..3b05b49a247e 100644 --- a/docs/examples/Qcodes example ATS_ONWORK.ipynb +++ b/docs/examples/Qcodes example ATS_ONWORK.ipynb @@ -402,13 +402,13 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 19, "metadata": { "collapsed": false }, "outputs": [], "source": [ - "ats_inst = ATSdriver.AlazarTech_ATS9360(name='Alazar1', server_name=None)" + "contr2 = ats_contr.HD_Controller(10e6)" ] }, { @@ -418,6 +418,17 @@ "collapsed": false }, "outputs": [], + "source": [ + "ats_inst = ATSdriver.AlazarTech_ATS9360(name='Alazar1', server_name=None)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "collapsed": false + }, + "outputs": [], "source": [ "ats_inst.config(clock_source='INTERNAL_CLOCK',\n", " sample_rate=500000000,\n", @@ -442,7 +453,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 30, "metadata": { "collapsed": false, "scrolled": false @@ -462,7 +473,6 @@ "made buffer list length 2\n", "completed AlazarPostAsyncBuffer\n", "Capturing 1 buffers.\n", - "not recycling\n", "buffers cleared\n" ] } @@ -487,26 +497,51 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 31, "metadata": { "collapsed": false }, "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "buffers cleared\n" - ] + "data": { + "text/plain": [ + "4340.0" + ] + }, + "execution_count": 31, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "resA[1]-resB[1]+180" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "array([ 57568., 58080., 58624., ..., 73280., 73248., 73184.])" + ] + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" } ], "source": [ - "ats_inst.clear_buffers()" + "resB" ] }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 33, "metadata": { "collapsed": false }, @@ -517,7 +552,7 @@ "12" ] }, - "execution_count": 8, + "execution_count": 33, "metadata": {}, "output_type": "execute_result" } @@ -528,7 +563,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 34, "metadata": { "collapsed": false }, @@ -539,7 +574,7 @@ "500000000" ] }, - "execution_count": 7, + "execution_count": 34, "metadata": {}, "output_type": "execute_result" } @@ -550,7 +585,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 35, "metadata": { "collapsed": false }, @@ -1294,7 +1329,7 @@ { "data": { "text/html": [ - "" + "" ], "text/plain": [ "" @@ -1306,21 +1341,21 @@ { "data": { "text/plain": [ - "" + "" ] }, - "execution_count": 7, + "execution_count": 35, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "qc.MatPlot(resA)" + "qc.MatPlot(resB)" ] }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 36, "metadata": { "collapsed": false }, @@ -2064,7 +2099,7 @@ { "data": { "text/html": [ - "" + "" ], "text/plain": [ "" @@ -2076,10 +2111,10 @@ { "data": { "text/plain": [ - "" + "" ] }, - "execution_count": 8, + "execution_count": 36, "metadata": {}, "output_type": "execute_result" } diff --git a/qcodes/instrument_drivers/AlazarTech/ATS.py b/qcodes/instrument_drivers/AlazarTech/ATS.py index 084ca36d14e9..ab9226663ac8 100644 --- a/qcodes/instrument_drivers/AlazarTech/ATS.py +++ b/qcodes/instrument_drivers/AlazarTech/ATS.py @@ -21,6 +21,7 @@ # TODO(nataliejpg) get_board_info and find_boards are broken?? # TODO(nataliejpg) call_dll error handling doesnt catch errors in expected way # TODO(nataliejpg) make use of _ATS_dll uniform +# TODO(nataliejpg) error if no trigger class AlazarTech_ATS(Instrument): # override dll_path in your init script or in the board constructor diff --git a/qcodes/instrument_drivers/AlazarTech/ATS_acquisition_controllers.py b/qcodes/instrument_drivers/AlazarTech/ATS_acquisition_controllers.py index 359a767624b0..09a2b2064177 100644 --- a/qcodes/instrument_drivers/AlazarTech/ATS_acquisition_controllers.py +++ b/qcodes/instrument_drivers/AlazarTech/ATS_acquisition_controllers.py @@ -6,12 +6,12 @@ # Heterodyne Measurement Controller # returns unprocessed data averaged by record for 2 channel use class HD_Controller(AcquisitionController): - def __init__(self): + def __init__(self, freq_dif): + self.freq_dif = freq_dif self.samples_per_record = None self.bits_per_sample = None self.records_per_buffer = None self.buffers_per_acquisition = None - self.allocated_buffers = None self.number_of_channels = 2 self.buffer = None @@ -24,7 +24,13 @@ def pre_start_capture(self, alazar): self.buffer = np.zeros(self.samples_per_record * self.records_per_buffer * self.number_of_channels) - + sample_speed = alazar.get_sample_speed() + integer_list = np.arange(self.samples_per_record) + angle_list = (2 * np.pi * self.freq_dif / sample_speed * + integer_list) + self.cos_list = np.cos(angle_list) + self.sin_list = np.sin(angle_list) + def pre_acquire(self, alazar): # gets called after 'AlazarStartCapture' pass @@ -50,7 +56,21 @@ def post_acquire(self, alazar): i0 = (i * self.samples_per_record * self.number_of_channels) + 1 i1 = i0 + self.samples_per_record *self.number_of_channels recordB += self.buffer[i0:i1:self.number_of_channels] / records_per_acquisition - return recordA, recordB + + resA = self.fit(recordA) + resB = self.fit(recordB) + + return resA, resB + + def fit(self, rec): + # Discrete Fourier Transform + RePart = np.dot(rec, self.cos_list) / self.samples_per_record + ImPart = np.dot(rec, self.sin_list) / self.samples_per_record + + ampl = np.sqrt(RePart ** 2 + ImPart ** 2) + phase = math.atan2(ImPart, RePart) * 360 / (2 * math.pi) + + return [ampl, phase] # Test AcquisitionController tested on ATS9360 (nataliejpg) @@ -58,19 +78,15 @@ def post_acquire(self, alazar): class Test_AcquisitionController(AcquisitionController): def __init__(self): self.samples_per_record = None - self.bits_per_sample = None self.records_per_buffer = None self.buffers_per_acquisition = None - self.allocated_buffers = None self.number_of_channels = 2 self.buffer = None def pre_start_capture(self, alazar): - self.bits_per_sample = alazar.get_idn()['bits_per_sample'] self.samples_per_record = alazar.samples_per_record() self.records_per_buffer = alazar.records_per_buffer() self.buffers_per_acquisition = alazar.buffers_per_acquisition() - sample_speed = alazar.get_sample_speed() self.buffer = np.zeros(self.samples_per_record * self.records_per_buffer * self.number_of_channels) @@ -101,7 +117,7 @@ def post_acquire(self, alazar): i1 = i0 + self.samples_per_record *self.number_of_channels recordB += self.buffer[i0:i1:self.number_of_channels] / records_per_acquisition return recordA, recordB - + # DFT AcquisitionController From b242aa078d9ea9700d1bdc460ab8d92a2b94e639 Mon Sep 17 00:00:00 2001 From: Natalie Pearson Date: Mon, 8 Aug 2016 18:07:40 +0200 Subject: [PATCH 05/17] First steps to filter acquisition controller and docstrings --- qcodes/instrument_drivers/AlazarTech/ATS.py | 2 +- .../AlazarTech/ATS_acquisition_controllers.py | 53 +++++---- .../AlazarTech/local_test.py | 109 ++++++++++++++++++ .../AlazarTech/local_test2.py | 93 +++++++++++++++ 4 files changed, 236 insertions(+), 21 deletions(-) create mode 100644 qcodes/instrument_drivers/AlazarTech/local_test.py create mode 100644 qcodes/instrument_drivers/AlazarTech/local_test2.py diff --git a/qcodes/instrument_drivers/AlazarTech/ATS.py b/qcodes/instrument_drivers/AlazarTech/ATS.py index ab9226663ac8..04905d5e5a4a 100644 --- a/qcodes/instrument_drivers/AlazarTech/ATS.py +++ b/qcodes/instrument_drivers/AlazarTech/ATS.py @@ -638,7 +638,7 @@ def signal_to_volt(self, channel, signal): return (((signal - 127.5) / 127.5) * (self.parameters['channel_range' + str(channel)].get())) - # TODO(nataliejpg) surely this should be get_sample_rate? + # TODO(nataliejpg) some explanation for why this is called speed not rate? def get_sample_speed(self): if self.sample_rate.get() == 'EXTERNAL_CLOCK': raise Exception('External clock is used, alazar driver ' diff --git a/qcodes/instrument_drivers/AlazarTech/ATS_acquisition_controllers.py b/qcodes/instrument_drivers/AlazarTech/ATS_acquisition_controllers.py index 09a2b2064177..fa6daa438ec4 100644 --- a/qcodes/instrument_drivers/AlazarTech/ATS_acquisition_controllers.py +++ b/qcodes/instrument_drivers/AlazarTech/ATS_acquisition_controllers.py @@ -2,31 +2,43 @@ import math import numpy as np - -# Heterodyne Measurement Controller -# returns unprocessed data averaged by record for 2 channel use class HD_Controller(AcquisitionController): + """Heterodyne Measurement Controller + Does averaged DFT on 2 channel Alazar measurement + + TODO(nataliejpg) handling of channel number + TODO(nataliejpg) test angle data + """ def __init__(self, freq_dif): self.freq_dif = freq_dif self.samples_per_record = None - self.bits_per_sample = None self.records_per_buffer = None self.buffers_per_acquisition = None self.number_of_channels = 2 self.buffer = None def pre_start_capture(self, alazar): - self.bits_per_sample = alazar.get_idn()['bits_per_sample'] + """Get config data from alazar card and set up DFT""" self.samples_per_record = alazar.samples_per_record() self.records_per_buffer = alazar.records_per_buffer() self.buffers_per_acquisition = alazar.buffers_per_acquisition() - sample_speed = alazar.get_sample_speed() + self.sample_rate = alazar.get_sample_speed() self.buffer = np.zeros(self.samples_per_record * self.records_per_buffer * self.number_of_channels) - sample_speed = alazar.get_sample_speed() + + # TODO(nataliejpg) leave super explicit or save lines? add error/logging? + averaging = self.buffers_per_acquisition * self.records_per_buffer + record_duration = self.samples_per_record/self.sample_rate + time_period_dif = 1/self.freq_dif + cycles_measured = record_duration / time_period_dif + oversampling_rate = self.sample_rate/(2*self.freq_dif) + print("Average over {} records".format(averaging)) + print("Oscillations per record: {} (expect 100+)".format(cycles_measured)) + print("Oversampling rate: {} (expect > 2)".format(oversampling_rate)) + integer_list = np.arange(self.samples_per_record) - angle_list = (2 * np.pi * self.freq_dif / sample_speed * + angle_list = (2 * np.pi * self.freq_dif / self.sample_rate * integer_list) self.cos_list = np.cos(angle_list) self.sin_list = np.sin(angle_list) @@ -39,10 +51,11 @@ def handle_buffer(self, alazar, data): self.buffer += data def post_acquire(self, alazar): - # average over records in buffer: - # for ATS9360 samples are arranged in the buffer as follows: - # S0A, S0B, ..., S1A, S1B, ... - # with SXY the sample number X of channel Y. + """Average over records in buffer and do DFT: + assumes samples are arranged in the buffer as follows: + S0A, S0B, ..., S1A, S1B, ... + with SXY the sample number X of channel Y. + """ records_per_acquisition = (1. * self.buffers_per_acquisition * self.records_per_buffer) recordA = np.zeros(self.samples_per_record) @@ -50,7 +63,6 @@ def post_acquire(self, alazar): i0 = i * self.samples_per_record * self.number_of_channels i1 = i0 + self.samples_per_record * self.number_of_channels recordA += self.buffer[i0:i1:self.number_of_channels] / records_per_acquisition - recordB = np.zeros(self.samples_per_record) for i in range(self.records_per_buffer): i0 = (i * self.samples_per_record * self.number_of_channels) + 1 @@ -63,19 +75,20 @@ def post_acquire(self, alazar): return resA, resB def fit(self, rec): - # Discrete Fourier Transform + """Do Discrete Fourier Transform and return magnitude and phase data""" RePart = np.dot(rec, self.cos_list) / self.samples_per_record ImPart = np.dot(rec, self.sin_list) / self.samples_per_record - - ampl = np.sqrt(RePart ** 2 + ImPart ** 2) - phase = math.atan2(ImPart, RePart) * 360 / (2 * math.pi) + # factor of 2 as amplitude is split between finite term and double frequency term + ampl = 2 * np.sqrt(RePart ** 2 + ImPart ** 2) + phase = math.atan2(ImPart, RePart) * 180 / (2 * math.pi) return [ampl, phase] -# Test AcquisitionController tested on ATS9360 (nataliejpg) -# returns unprocessed data averaged by record for 2 channel use -class Test_AcquisitionController(AcquisitionController): +class Basic_AcquisitionController(AcquisitionController): + """Basic AcquisitionController tested on ATS9360 + returns unprocessed data averaged by record with 2 channels + """ def __init__(self): self.samples_per_record = None self.records_per_buffer = None diff --git a/qcodes/instrument_drivers/AlazarTech/local_test.py b/qcodes/instrument_drivers/AlazarTech/local_test.py new file mode 100644 index 000000000000..6e85961fe531 --- /dev/null +++ b/qcodes/instrument_drivers/AlazarTech/local_test.py @@ -0,0 +1,109 @@ +from .ATS import AcquisitionController +import math +import numpy as np +import matplotlib.pyplot as plt + + +class Controller(): + def __init__(self, dif_freq): + self.dif_freq = dif_freq + self.sample_rate = 500000000 + self.samples_per_record = 1024 + + record_duration = self.samples_per_record/self.sample_rate + dif_time_period = 1/self.dif_freq + cycles_measured = record_duration / dif_time_period + oversampling_rate = self.sample_rate/(2*self.dif_freq) + print("Measuring from "+str(cycles_measured)+" samples should be on the order of 100 at least") + print("Oversampling rate is "+str(oversampling_rate)+" should be > 2") + + def pre_start_capture(self): + self.integer_list = np.arange(self.samples_per_record) + self.angle_list = (2 * np.pi * self.dif_freq / self.sample_rate * + self.integer_list) + + self.cos_list = np.cos(self.angle_list) + self.sin_list = np.sin(self.angle_list) + + def fit(self, rec): + # Discrete Fourier Transform + RePart = np.dot(rec, self.cos_list) / self.samples_per_record + ImPart = np.dot(rec, self.sin_list) / self.samples_per_record + # factor of 2 is because amplitude is split between finite term and + # double frequency term which averages to 0 + ampl = 2 * np.sqrt(RePart ** 2 + ImPart ** 2) + phase = math.atan2(ImPart, RePart) * 180 / (2 * math.pi) + + return [ampl, phase] + + def pre_acquire(self, alazar): + # gets called after 'AlazarStartCapture' + pass + + def handle_buffer(self, alazar, data): + self.buffer += data + + def post_acquire(self, alazar): + # average over records in buffer: + # for ATS9360 samples are arranged in the buffer as follows: + # S0A, S0B, ..., S1A, S1B, ... + # with SXY the sample number X of channel Y. + records_per_acquisition = (1. * self.buffers_per_acquisition * + self.records_per_buffer) + recordA = np.zeros(self.samples_per_record) + for i in range(self.records_per_buffer): + i0 = i * self.samples_per_record * self.number_of_channels + i1 = i0 + self.samples_per_record * self.number_of_channels + recordA += self.buffer[i0:i1:self.number_of_channels] / records_per_acquisition + + recordB = np.zeros(self.samples_per_record) + for i in range(self.records_per_buffer): + i0 = (i * self.samples_per_record * self.number_of_channels) + 1 + i1 = i0 + self.samples_per_record *self.number_of_channels + recordB += self.buffer[i0:i1:self.number_of_channels] / records_per_acquisition + + resA = self.fit(recordA) + resB = self.fit(recordB) + + return resA, resB + + +def make_rec(controller, freq, phase_deg): + phase_rad = phase_deg*2*np.pi/180 + angle_list = (2 * np.pi * freq / controller.sample_rate * + controller.integer_list)+phase_rad + rec = np.cos(angle_list) + return rec + +def main(freq1, phase): + c = Controller(freq1) + c.pre_start_capture() + ran = 100 + l = [[],[]] + for i in range(ran+1): + freq = freq1-0.002*freq1*(ran/2-i) + rec = make_rec(c, freq, phase) + mag, phase = c.fit(rec) + l[0].append(mag) + l[1].append(phase) + plt.figure(1) + plt.subplot(211) + plt.plot(l[0]) + plt.subplot(212) + plt.plot(l[1]) + plt.show() + # cos_rec = np.multiply(c.cos_list, rec) + # sin_rec = np.multiply(c.sin_list, rec) + #print(c.fit(rec)) + # plt.figure(1) + # plt.subplot(511) + # plt.plot(c.cos_list) + # plt.subplot(512) + # plt.plot(c.sin_list) + # plt.subplot(513) + # plt.plot(rec) + # plt.subplot(514) + # plt.plot(cos_rec) + # plt.subplot(515) + # plt.plot(sin_rec) + # plt.show() diff --git a/qcodes/instrument_drivers/AlazarTech/local_test2.py b/qcodes/instrument_drivers/AlazarTech/local_test2.py new file mode 100644 index 000000000000..9ce6f502e193 --- /dev/null +++ b/qcodes/instrument_drivers/AlazarTech/local_test2.py @@ -0,0 +1,93 @@ +from .ATS import AcquisitionController +import math +import numpy as np +import matplotlib.pyplot as plt +from scipy import signal + + +class Controller(): + def __init__(self, dif_freq): + self.dif_freq = dif_freq + self.sample_rate = 5000000 + self.samples_per_record = 10024 + + record_duration = self.samples_per_record/self.sample_rate + dif_time_period = 1/self.dif_freq + cycles_measured = record_duration / dif_time_period + oversampling_rate = self.sample_rate/(2*self.dif_freq) + print("Measuring from "+str(cycles_measured)+" samples should be on the order of 100 at least") + print("Oversampling rate is "+str(oversampling_rate)+" should be > 2") + + def pre_start_capture(self): + self.integer_list = np.arange(self.samples_per_record) + self.angle_list = (2 * np.pi * self.dif_freq / self.sample_rate * + self.integer_list) + + self.cos_list = np.cos(self.angle_list) + self.sin_list = np.sin(self.angle_list) + + def pre_acquire(self, alazar): + # gets called after 'AlazarStartCapture' + pass + + def handle_buffer(self, alazar, data): + self.buffer += data + + def post_acquire(self, alazar): + # average over records in buffer: + # for ATS9360 samples are arranged in the buffer as follows: + # S0A, S0B, ..., S1A, S1B, ... + # with SXY the sample number X of channel Y. + records_per_acquisition = (1. * self.buffers_per_acquisition * + self.records_per_buffer) + recordA = np.zeros(self.samples_per_record) + for i in range(self.records_per_buffer): + i0 = i * self.samples_per_record * self.number_of_channels + i1 = i0 + self.samples_per_record * self.number_of_channels + recordA += self.buffer[i0:i1:self.number_of_channels] / records_per_acquisition + + recordB = np.zeros(self.samples_per_record) + for i in range(self.records_per_buffer): + i0 = (i * self.samples_per_record * self.number_of_channels) + 1 + i1 = i0 + self.samples_per_record *self.number_of_channels + recordB += self.buffer[i0:i1:self.number_of_channels] / records_per_acquisition + + resA = self.fit(recordA) + resB = self.fit(recordB) + + return resA, resB + +def make_rec(controller, freq, phase_deg): + phase_rad = phase_deg*2*np.pi/180 + angle_list = (2 * np.pi * freq / controller.sample_rate * + controller.integer_list)+phase_rad + rec = np.cos(angle_list) + return rec + +def main(freq1, freq2): + c = Controller(freq1) + c.pre_start_capture() + rec1 = make_rec(c, freq2, 0) + rec2 = c.cos_list + plt.figure(1) + plt.subplot(211) + plt.plot(rec1) + plt.subplot(212) + plt.plot(rec1) + plt.show() + s = np.multiply(rec1, rec2) + plt.plot(s) + plt.show() + + +n = 61 +a = signal.firwin(n, cutoff = 0.3, window = "hamming") +#Frequency and phase response +mfreqz(a) +show() +#Impulse and step response +figure(2) +impz(a) +show() + + # import qcodes.instrument_drivers.AlazarTech.local_test2 as cont From 3a27d4396a5362baf99b401351e554bbaa472f11 Mon Sep 17 00:00:00 2001 From: Natalie Pearson Date: Thu, 11 Aug 2016 19:09:32 +0200 Subject: [PATCH 06/17] feat: variety of QDev acquisition controllers created, not complete --- .../QDev_acquisition_controllers.py | 246 ++++++++++++++++++ 1 file changed, 246 insertions(+) create mode 100644 qcodes/instrument_drivers/AlazarTech/QDev_acquisition_controllers.py diff --git a/qcodes/instrument_drivers/AlazarTech/QDev_acquisition_controllers.py b/qcodes/instrument_drivers/AlazarTech/QDev_acquisition_controllers.py new file mode 100644 index 000000000000..c34bc08080c6 --- /dev/null +++ b/qcodes/instrument_drivers/AlazarTech/QDev_acquisition_controllers.py @@ -0,0 +1,246 @@ +from .ATS import AcquisitionController +from scipy import signal +import numpy as np + + +class Filtering_Controller(): + """Filtering Measurement Controller + If 1 channel: + Mixes reference software signal with input hardware signal, + filters out double frequency component and returns magnitude and + phase data for dc component. Throws away channel 2 + If 2 channels: + Uses two hardware channels to find magnitude and + phase data for dc component + + TODO(nataliejpg) test diffrent filters and window types + TODO(nataliejpg) numtaps logic + TODO(nataliejpg) cutoff logic + TODO(nataliejpg) post aqcuire one input stuff?? + """ + + def __init__(self, dif_freq, numtaps, cutoff, number_of_channels): + self.dif_freq = dif_freq + self.sample_rate = 50000000 + self.samples_per_record = 10024 + self.buffers_per_acquisition = None + self.number_of_channels = number_of_channels + self.numtaps = numtaps + self.cutoff = cutoff + self.delay = numtaps - 1 + self.buffer = None + + def pre_start_capture(self): + record_duration = self.samples_per_record / self.sample_rate + time_period_dif = 1 / self.dif_freq + cycles_measured = record_duration / time_period_dif + oversampling_rate = self.sample_rate / (2 * self.dif_freq) + print("Oscillations per record: {:.2f} (expect 100+)" + .format(cycles_measured)) + print("Oversampling rate: {:.2f} (expect > 2)" + .format(oversampling_rate)) + print("Filtering delay is {} samples".format(self.delay)) + + integer_list = np.arange(self.samples_per_record) + if self.number_of_channels == 2: + angle_list = (2 * np.pi * self.freq_dif / self.sample_rate * + integer_list) + self.cos_list = np.cos(angle_list) + self.sin_list = np.sin(angle_list) + elif self.number_of_channels == 1: + angle_shift = 90 + self.sample_shift = (2 * np.pi * self.freq_dif / self.sample_rate * + angle_shift) + + def pre_acquire(self, alazar): + # gets called after 'AlazarStartCapture' + pass + + def handle_buffer(self, alazar, data): + self.buffer += data + + def post_acquire(self, alazar): + """Average over records in buffer, mix and filter double freq + if 1 chan selected recordA is mixed with software signal + if 2 chan selected recordA and recordB are mixed + """ + records_per_acquisition = (1. * self.buffers_per_acquisition * + self.records_per_buffer) + recordA = np.zeros(self.samples_per_record) + for i in range(self.records_per_buffer): + i0 = (i * self.samples_per_record * self.number_of_channels) + i1 = (i0 + self.samples_per_record * self.number_of_channels) + recordA += (self.buffer[i0:i1:self.number_of_channels] / + records_per_acquisition) + recordB = np.zeros(self.samples_per_record) + for i in range(self.records_per_buffer): + i0 = (i * self.samples_per_record * self.number_of_channels + 1) + i1 = (i0 + self.samples_per_record * self.number_of_channels) + recordB += (self.buffer[i0:i1:self.number_of_channels] / + records_per_acquisition) + + if self.number_of_channels == 1: + mixA_re = np.multiply(self.cos_list, recordA) + mixA_im = np.multiply(self.sin_list, recordA) + mixA_re_filtered = filter(mixA_re) + mixA_im_filtered = filter(mixA_im) + mixA_complex = mixA_re_filtered + mixA_im_filtered * 1j + magA = 2 * abs(mixA_complex) + phaseA = 2 * np.angle(mixA_complex, deg=True) + return magA, phaseA + elif self.number_of_channels == 2: + recordB_shifted = np.roll(recordB, self.sample_shift) + mixAB_re = np.multiply(recordA, recordB) + mixAB_im = np.multiply(recordA, recordB_shifted) + mixAB_re_filtered = filter(mixAB_re) + mixAB_im_filtered = filter(mixAB_im) + mixAB_complex = mixAB_re_filtered + mixAB_im_filtered * 1j + magAB = 2 * abs(mixAB_complex) + phaseAB = 2 * np.angle(mixAB_complex, deg=True) + return magAB, phaseAB + + def filter(self, rec): + """FIR window filter applied to filter out high freq components + """ + sample_rate = self.sample_rate + nyq_rate = sample_rate / 2. + fir_coef = signal.firwin(self.numtaps, self.cutoff / nyq_rate) + filtered_rec = signal.lfilter(fir_coef, 1.0, rec) + return filtered_rec + + +class HD_Controller(AcquisitionController): + """Averaging Heterodyne Measurement Controller + Does averaged DFT on 2 channel Alazar measurement + + TODO(nataliejpg) handling of channel number + TODO(nataliejpg) test angle data + """ + + def __init__(self, freq_dif): + self.freq_dif = freq_dif + self.samples_per_record = None + self.records_per_buffer = None + self.buffers_per_acquisition = None + self.number_of_channels = 2 + self.buffer = None + + def pre_start_capture(self, alazar): + """Get config data from alazar card and set up DFT""" + self.samples_per_record = alazar.samples_per_record() + self.records_per_buffer = alazar.records_per_buffer() + self.buffers_per_acquisition = alazar.buffers_per_acquisition() + self.sample_rate = alazar.get_sample_speed() + self.buffer = np.zeros(self.samples_per_record * + self.records_per_buffer * + self.number_of_channels) + # TODO(nataliejpg) leave explicit or save lines? add error/logging? + averaging = self.buffers_per_acquisition * self.records_per_buffer + record_duration = self.samples_per_record / self.sample_rate + time_period_dif = 1 / self.freq_dif + cycles_measured = record_duration / time_period_dif + oversampling_rate = self.sample_rate / (2 * self.freq_dif) + print("Average over {:.2f} records".format(averaging)) + print("Oscillations per record: {:.2f} (expect 100+)" + .format(cycles_measured)) + print("Oversampling rate: {:.2f} (expect > 2)" + .format(oversampling_rate)) + + integer_list = np.arange(self.samples_per_record) + angle_list = (2 * np.pi * self.freq_dif / self.sample_rate * + integer_list) + self.cos_list = np.cos(angle_list) + self.sin_list = np.sin(angle_list) + + def pre_acquire(self, alazar): + # gets called after 'AlazarStartCapture' + pass + + def handle_buffer(self, alazar, data): + self.buffer += data + + def post_acquire(self, alazar): + """Average over records in buffer and do DFT: + assumes samples are arranged in the buffer as follows: + S0A, S0B, ..., S1A, S1B, ... + with SXY the sample number X of channel Y. + """ + records_per_acquisition = (1. * self.buffers_per_acquisition * + self.records_per_buffer) + recordA = np.zeros(self.samples_per_record) + for i in range(self.records_per_buffer): + i0 = (i * self.samples_per_record * self.number_of_channels) + i1 = (i0 + self.samples_per_record * self.number_of_channels) + recordA += (self.buffer[i0:i1:self.number_of_channels] / + records_per_acquisition) + recordB = np.zeros(self.samples_per_record) + for i in range(self.records_per_buffer): + i0 = (i * self.samples_per_record * self.number_of_channels + 1) + i1 = (i0 + self.samples_per_record * self.number_of_channels) + recordB += (self.buffer[i0:i1:self.number_of_channels] / + records_per_acquisition) + + resA = self.fit(recordA) + resB = self.fit(recordB) + + return resA, resB + + def fit(self, rec): + """Do Discrete Fourier Transform and return magnitude and phase data""" + RePart = np.dot(rec, self.cos_list) / self.samples_per_record + ImPart = np.dot(rec, self.sin_list) / self.samples_per_record + complex_num = RePart - ImPart * 1j + # factor of 2 in ampl is due to loss of averaged double frequency term + ampl = 2 * abs(complex_num) + phase = np.angle(complex_num, deg=True) + return [ampl, phase] + + +class Basic_AcquisitionController(AcquisitionController): + """Basic AcquisitionController tested on ATS9360 + returns unprocessed data averaged by record with 2 channels + """ + + def __init__(self): + self.samples_per_record = None + self.records_per_buffer = None + self.buffers_per_acquisition = None + self.number_of_channels = 2 + self.buffer = None + + def pre_start_capture(self, alazar): + self.samples_per_record = alazar.samples_per_record() + self.records_per_buffer = alazar.records_per_buffer() + self.buffers_per_acquisition = alazar.buffers_per_acquisition() + self.buffer = np.zeros(self.samples_per_record * + self.records_per_buffer * + self.number_of_channels) + + def pre_acquire(self, alazar): + # gets called after 'AlazarStartCapture' + pass + + def handle_buffer(self, alazar, data): + self.buffer += data + + def post_acquire(self, alazar): + # average over records in buffer: + # for ATS9360 samples are arranged in the buffer as follows: + # S0A, S0B, ..., S1A, S1B, ... + # with SXY the sample number X of channel Y. + records_per_acquisition = (self.buffers_per_acquisition * + self.records_per_buffer) + recordA = np.zeros(self.samples_per_record) + for i in range(self.records_per_buffer): + i0 = (i * self.samples_per_record * self.number_of_channels) + i1 = (i0 + self.samples_per_record * self.number_of_channels) + recordA += (self.buffer[i0:i1:self.number_of_channels] / + records_per_acquisition) + + recordB = np.zeros(self.samples_per_record) + for i in range(self.records_per_buffer): + i0 = (i * self.samples_per_record * self.number_of_channels + 1) + i1 = (i0 + self.samples_per_record * self.number_of_channels) + recordB += (self.buffer[i0:i1:self.number_of_channels] / + records_per_acquisition) + return recordA, recordB From 23cde4e962878b14fc34edb549e039077b340eb6 Mon Sep 17 00:00:00 2001 From: Natalie Pearson Date: Mon, 15 Aug 2016 09:38:52 +0200 Subject: [PATCH 07/17] refactor/style: comments and docstings tidy up and temporary test refactor --- qcodes/instrument_drivers/AlazarTech/ATS.py | 3 +- .../instrument_drivers/AlazarTech/ATS9360.py | 16 ++- .../AlazarTech/ATS_acquisition_controllers.py | 68 +++++----- .../AlazarTech/HD_test_controller.py | 105 +++++++++++++++ .../AlazarTech/filter1_test_controller.py | 124 +++++++++++++++++ .../AlazarTech/filter2_test_controller.py | 126 ++++++++++++++++++ .../AlazarTech/local_test.py | 109 --------------- .../AlazarTech/local_test2.py | 93 ------------- 8 files changed, 403 insertions(+), 241 deletions(-) create mode 100644 qcodes/instrument_drivers/AlazarTech/HD_test_controller.py create mode 100644 qcodes/instrument_drivers/AlazarTech/filter1_test_controller.py create mode 100644 qcodes/instrument_drivers/AlazarTech/filter2_test_controller.py delete mode 100644 qcodes/instrument_drivers/AlazarTech/local_test.py delete mode 100644 qcodes/instrument_drivers/AlazarTech/local_test2.py diff --git a/qcodes/instrument_drivers/AlazarTech/ATS.py b/qcodes/instrument_drivers/AlazarTech/ATS.py index 04905d5e5a4a..df0940f3a881 100644 --- a/qcodes/instrument_drivers/AlazarTech/ATS.py +++ b/qcodes/instrument_drivers/AlazarTech/ATS.py @@ -23,6 +23,7 @@ # TODO(nataliejpg) make use of _ATS_dll uniform # TODO(nataliejpg) error if no trigger + class AlazarTech_ATS(Instrument): # override dll_path in your init script or in the board constructor # if you have it somewhere else @@ -193,7 +194,7 @@ def get_board_info(cls, dll, system_id, board_id): def __init__(self, name, system_id=1, board_id=1, dll_path=None, **kwargs): super().__init__(name, **kwargs) self._ATS_dll = None - + if os.name == 'nt': self._ATS_dll = ctypes.cdll.LoadLibrary(dll_path or self.dll_path) else: diff --git a/qcodes/instrument_drivers/AlazarTech/ATS9360.py b/qcodes/instrument_drivers/AlazarTech/ATS9360.py index 5703d92a6bd5..a564c01a9fbb 100644 --- a/qcodes/instrument_drivers/AlazarTech/ATS9360.py +++ b/qcodes/instrument_drivers/AlazarTech/ATS9360.py @@ -6,8 +6,7 @@ class AlazarTech_ATS9360(AlazarTech_ATS): def __init__(self, name, server_name=None): dll_path = 'C:\\WINDOWS\\System32\\ATSApi.dll' super().__init__(name, dll_path=dll_path) - - samplesPerSec = None + # add parameters # ----- Parameters for the configuration of the board ----- @@ -133,7 +132,7 @@ def __init__(self, name, server_name=None): unit='Sample clock cycles', value=0, vals=validators.Ints(min_value=0)) - + # NOTE: The board will wait for a for this amount of time for a # trigger event. If a trigger event does not arrive, then the # board will automatically trigger. Set the trigger timeout value @@ -149,7 +148,6 @@ def __init__(self, name, server_name=None): unit='10 us', value=0, vals=validators.Ints(min_value=0)) - # ----- Parameters for the acquire function ----- self.add_parameter(name='mode', @@ -169,8 +167,12 @@ def __init__(self, name, server_name=None): # TODO (M) figure out if this also has to be a multiple of something, # I could not find this in the documentation but somehow I have the # feeling it still should be a multiple of something - # NOTE by ramiro: At least in previous python implementations(PycQED delft), this is an artifact for compatibility with AWG sequencing, not particular to any ATS architecture. - # ==> this is a construction imposed by the memory strategy implemented on the python driver we are writing, not limited by any actual ATS feature. + # NOTE by ramiro: At least in previous python implementations(PycQED delft), + # this is an artifact for compatibility with AWG sequencing, + # not particular to any ATS architecture. + # ==> this is a construction imposed by the memory strategy + # implemented on the python driver + # we are writing, not limited by any actual ATS feature. self.add_parameter(name='records_per_buffer', parameter_class=AlazarParameter, @@ -255,7 +257,7 @@ def __init__(self, name, server_name=None): # TODO (M) make parameter for board type # TODO (M) check board kind - + class Multiples(validators.Ints): ''' diff --git a/qcodes/instrument_drivers/AlazarTech/ATS_acquisition_controllers.py b/qcodes/instrument_drivers/AlazarTech/ATS_acquisition_controllers.py index fa6daa438ec4..538cc9b93eaf 100644 --- a/qcodes/instrument_drivers/AlazarTech/ATS_acquisition_controllers.py +++ b/qcodes/instrument_drivers/AlazarTech/ATS_acquisition_controllers.py @@ -2,13 +2,15 @@ import math import numpy as np + class HD_Controller(AcquisitionController): """Heterodyne Measurement Controller - Does averaged DFT on 2 channel Alazar measurement + Does averaged DFT on 2 channel Alazar measurement TODO(nataliejpg) handling of channel number TODO(nataliejpg) test angle data """ + def __init__(self, freq_dif): self.freq_dif = freq_dif self.samples_per_record = None @@ -24,17 +26,17 @@ def pre_start_capture(self, alazar): self.buffers_per_acquisition = alazar.buffers_per_acquisition() self.sample_rate = alazar.get_sample_speed() self.buffer = np.zeros(self.samples_per_record * - self.records_per_buffer * - self.number_of_channels) - - # TODO(nataliejpg) leave super explicit or save lines? add error/logging? + self.records_per_buffer * + self.number_of_channels) + # TODO(nataliejpg) leave explicit or save lines? add error/logging? averaging = self.buffers_per_acquisition * self.records_per_buffer - record_duration = self.samples_per_record/self.sample_rate - time_period_dif = 1/self.freq_dif + record_duration = self.samples_per_record / self.sample_rate + time_period_dif = 1 / self.freq_dif cycles_measured = record_duration / time_period_dif - oversampling_rate = self.sample_rate/(2*self.freq_dif) + oversampling_rate = self.sample_rate / (2 * self.freq_dif) print("Average over {} records".format(averaging)) - print("Oscillations per record: {} (expect 100+)".format(cycles_measured)) + print("Oscillations per record: {} (expect 100+)" + .format(cycles_measured)) print("Oversampling rate: {} (expect > 2)".format(oversampling_rate)) integer_list = np.arange(self.samples_per_record) @@ -42,7 +44,7 @@ def pre_start_capture(self, alazar): integer_list) self.cos_list = np.cos(angle_list) self.sin_list = np.sin(angle_list) - + def pre_acquire(self, alazar): # gets called after 'AlazarStartCapture' pass @@ -60,28 +62,30 @@ def post_acquire(self, alazar): self.records_per_buffer) recordA = np.zeros(self.samples_per_record) for i in range(self.records_per_buffer): - i0 = i * self.samples_per_record * self.number_of_channels - i1 = i0 + self.samples_per_record * self.number_of_channels - recordA += self.buffer[i0:i1:self.number_of_channels] / records_per_acquisition + i0 = (i * self.samples_per_record * self.number_of_channels) + i1 = (i0 + self.samples_per_record * self.number_of_channels) + recordA += (self.buffer[i0:i1:self.number_of_channels] / + records_per_acquisition) recordB = np.zeros(self.samples_per_record) for i in range(self.records_per_buffer): - i0 = (i * self.samples_per_record * self.number_of_channels) + 1 - i1 = i0 + self.samples_per_record *self.number_of_channels - recordB += self.buffer[i0:i1:self.number_of_channels] / records_per_acquisition - + i0 = (i * self.samples_per_record * self.number_of_channels + 1) + i1 = (i0 + self.samples_per_record * self.number_of_channels) + recordB += (self.buffer[i0:i1:self.number_of_channels] / + records_per_acquisition) + resA = self.fit(recordA) resB = self.fit(recordB) - + return resA, resB - + def fit(self, rec): """Do Discrete Fourier Transform and return magnitude and phase data""" RePart = np.dot(rec, self.cos_list) / self.samples_per_record ImPart = np.dot(rec, self.sin_list) / self.samples_per_record - # factor of 2 as amplitude is split between finite term and double frequency term + # factor of 2 as amplitude is split between finite term + # and double frequency term ampl = 2 * np.sqrt(RePart ** 2 + ImPart ** 2) phase = math.atan2(ImPart, RePart) * 180 / (2 * math.pi) - return [ampl, phase] @@ -89,6 +93,7 @@ class Basic_AcquisitionController(AcquisitionController): """Basic AcquisitionController tested on ATS9360 returns unprocessed data averaged by record with 2 channels """ + def __init__(self): self.samples_per_record = None self.records_per_buffer = None @@ -101,8 +106,8 @@ def pre_start_capture(self, alazar): self.records_per_buffer = alazar.records_per_buffer() self.buffers_per_acquisition = alazar.buffers_per_acquisition() self.buffer = np.zeros(self.samples_per_record * - self.records_per_buffer * - self.number_of_channels) + self.records_per_buffer * + self.number_of_channels) def pre_acquire(self, alazar): # gets called after 'AlazarStartCapture' @@ -116,21 +121,22 @@ def post_acquire(self, alazar): # for ATS9360 samples are arranged in the buffer as follows: # S0A, S0B, ..., S1A, S1B, ... # with SXY the sample number X of channel Y. - records_per_acquisition = (1. * self.buffers_per_acquisition * + records_per_acquisition = (self.buffers_per_acquisition * self.records_per_buffer) recordA = np.zeros(self.samples_per_record) for i in range(self.records_per_buffer): - i0 = i * self.samples_per_record * self.number_of_channels - i1 = i0 + self.samples_per_record * self.number_of_channels - recordA += self.buffer[i0:i1:self.number_of_channels] / records_per_acquisition + i0 = (i * self.samples_per_record * self.number_of_channels) + i1 = (i0 + self.samples_per_record * self.number_of_channels) + recordA += (self.buffer[i0:i1:self.number_of_channels] / + records_per_acquisition) recordB = np.zeros(self.samples_per_record) for i in range(self.records_per_buffer): - i0 = (i * self.samples_per_record * self.number_of_channels) + 1 - i1 = i0 + self.samples_per_record *self.number_of_channels - recordB += self.buffer[i0:i1:self.number_of_channels] / records_per_acquisition + i0 = (i * self.samples_per_record * self.number_of_channels + 1) + i1 = (i0 + self.samples_per_record * self.number_of_channels) + recordB += (self.buffer[i0:i1:self.number_of_channels] / + records_per_acquisition) return recordA, recordB - # DFT AcquisitionController diff --git a/qcodes/instrument_drivers/AlazarTech/HD_test_controller.py b/qcodes/instrument_drivers/AlazarTech/HD_test_controller.py new file mode 100644 index 000000000000..f9b4e751cd16 --- /dev/null +++ b/qcodes/instrument_drivers/AlazarTech/HD_test_controller.py @@ -0,0 +1,105 @@ +from .ATS import AcquisitionController +import math +import numpy as np +import matplotlib.pyplot as plt + +# HD_ test controller +class Controller(): + def __init__(self, dif_freq): + self.dif_freq = dif_freq + self.sample_rate = 10000000 + self.samples_per_record = 2024 + + record_duration = self.samples_per_record / self.sample_rate + time_period_dif = 1 / self.dif_freq + cycles_measured = record_duration / time_period_dif + oversampling_rate = self.sample_rate / (2 * self.dif_freq) + print("Oscillations per record: {:.2f} (expect 100+)".format(cycles_measured)) + print("Oversampling rate: {:.2f} (expect > 2)".format(oversampling_rate)) + + def pre_start_capture(self): + self.integer_list = np.arange(self.samples_per_record) + self.angle_list = (2 * np.pi * self.dif_freq / self.sample_rate * + self.integer_list) + + self.cos_list = np.cos(self.angle_list) + self.sin_list = np.sin(self.angle_list) + + def fit(self, rec): + # Discrete Fourier Transform + RePart = np.dot(rec, self.cos_list) / self.samples_per_record + ImPart = np.dot(rec, self.sin_list) / self.samples_per_record + # factor of 2 is because amplitude is split between finite term and + # double frequency term which averages to 0 + complex_num = RePart - ImPart*1j + ampl = 2 * abs(complex_num) + phase = np.angle(complex_num, deg=True) + + return [ampl, phase] + +def make_rec(controller, freq, phase_deg): + phase_rad = phase_deg*np.pi/180 + angle_list = (2 * np.pi * freq / controller.sample_rate * + controller.integer_list)+phase_rad + rec = np.cos(angle_list) + return rec + +def main(freq, phase_dif): + # make an acqisition controller and set up sin and cos software signals + c = Controller(freq) + c.pre_start_capture() + + # make a 'hardware' signal with same frequency shifted by phase + # mix with software signals to make real and imaginary parts of mixed signal + rec = make_rec(c, freq, phase_dif) + cos_rec = np.multiply(c.cos_list, rec) + sin_rec = np.multiply(c.sin_list, rec) + + # print averaged magnitude and phase of mixed signal + # should give dc part of signal as double frequency part averages out + print(" mag, phase : "+str(c.fit(rec))) + plt.figure(1) + plt.subplot(311) + plt.plot(c.cos_list[:100], "-r") + plt.plot(c.sin_list[:100], "-g") + plt.title('"software" signals') + plt.subplot(312) + plt.plot(rec[:100]) + plt.title('"hardware" signal') + plt.subplot(313) + plt.plot(cos_rec[:100], "-r") + plt.plot(sin_rec[:100], "-g") + plt.title('re and im parts of mixed signal') + plt.show() + + # sweep 'hardware' signal frequency through software signal and + # observe magnitude and phase response + point_ran = 10000 + sweep_mag = [[],[]] + for i in range(point_ran+1): + freq_ran = 0.2*freq + shift_freq = (freq_ran/point_ran)*(-point_ran/2+i) + rec = make_rec(c, freq+shift_freq, 0) + mag, phase = c.fit(rec) + sweep_mag[1].append(mag) + sweep_mag[0].append(shift_freq) + sweep_phase = [[],[]] + for i in range(point_ran+1): + angle_ran = 45 + shift_phase = (angle_ran/point_ran)*(-point_ran/2+i) + rec = make_rec(c, freq, phase_dif+shift_phase) + mag, phase = c.fit(rec) + sweep_phase[1].append(phase) + sweep_phase[0].append(shift_phase) + plt.figure(1) + plt.subplot(211) + plt.title('sweep:magnitude response') + plt.plot(sweep_mag[0], sweep_mag[1]) + plt.subplot(212) + plt.title('sweep:phase response') + plt.plot(sweep_phase[0], sweep_phase[1]) + plt.show() + + + + # import qcodes.instrument_drivers.AlazarTech.HD_test_controller as cont diff --git a/qcodes/instrument_drivers/AlazarTech/filter1_test_controller.py b/qcodes/instrument_drivers/AlazarTech/filter1_test_controller.py new file mode 100644 index 000000000000..6efae1d80321 --- /dev/null +++ b/qcodes/instrument_drivers/AlazarTech/filter1_test_controller.py @@ -0,0 +1,124 @@ +import numpy as np +import matplotlib.pyplot as plt +from scipy import signal + +# one channel filter controller +class Controller(): + def __init__(self, dif_freq): + self.dif_freq = dif_freq + self.sample_rate = 500000000 + self.samples_per_record = 10024 + + record_duration = self.samples_per_record/self.sample_rate + dif_time_period = 1/self.dif_freq + cycles_measured = record_duration / dif_time_period + oversampling_rate = self.sample_rate/(2*self.dif_freq) + print("Measuring from "+str(cycles_measured)+" samples should be on the order of 100 at least") + print("Oversampling rate is "+str(oversampling_rate)+" should be > 2") + + def pre_start_capture(self): + self.integer_list = np.arange(self.samples_per_record) + self.angle_list = (2 * np.pi * self.dif_freq / self.sample_rate * + self.integer_list) + + self.cos_list = np.cos(self.angle_list) + self.sin_list = np.sin(self.angle_list) + +def filter(controller, rec, numtaps, cutoff): + sample_rate = controller.sample_rate + print("sample_rate is "+str(sample_rate)) + nyq_rate = sample_rate / 2. + print("nyq_rate is "+ str(nyq_rate)) + delay = (numtaps-1) + fir_coef = signal.firwin(numtaps, cutoff/nyq_rate, window = "hamming") + filtered_rec = 2 * signal.lfilter(fir_coef, 1.0, rec) + w, h = signal.freqz(fir_coef) + plt.plot(w*controller.sample_rate/(2*np.pi), 20 * np.log10(abs(h)), 'b') + plt.ylabel('Amplitude [dB]', color='b') + plt.xlabel('Frequency [rad/sample]') + plt.show() + return fir_coef, filtered_rec, delay + +def make_rec(controller, freq, ampdif, phase_deg): + phase_rad = phase_deg*2*np.pi/360 + angle_list = (2 * np.pi * freq * controller.integer_list / controller.sample_rate)+phase_rad + rec = ampdif*np.cos(angle_list) + return rec + +def main(freq1, freq2, phasedif, ampdif, numtaps, cutoff): + # make an acquisition controller (and sampling rate warnings related to + # given freq1) and integer list used for signal making + c = Controller(freq1) + c.pre_start_capture() + + # make one hardware signal with given frequency and phase difference + # make a software signal with real and imaginary parts + # plot first 100 points of each + rec1 = make_rec(c, freq2, phase_deg=phasedif, ampdif=ampdif) + plt.figure(1) + plt.subplot(211) + plt.plot(rec1[:100]) + plt.subplot(212) + plt.plot(rec2[:100]) + plt.show() + + # mix hardware signals and filter out frequencies above cutoff + # should leave only freq_dif component + rec12 = np.multiply(rec1, rec2) + fir_coef, filtered_rec_12 = filter(c, rec12, numtaps, cutoff) + delay = numtaps-1 + + # plot filter + w, h = signal.freqz(fir_coef) + freq_axis = (w/np.pi)*c.sample_rate + fig = plt.figure(1) + ax1 = fig.add_subplot(111) + plt.title('Digital filter frequency response') + plt.plot(freq_axis, 20 * np.log10(abs(h)), 'b') + plt.ylabel('Amplitude [dB]', color='b') + plt.xlabel('Frequency [Hz]') + + ax2 = ax1.twinx() + angles = np.unwrap(np.angle(h)) + plt.plot(freq_axis, angles, 'g') + plt.ylabel('Angle (radians)', color='g') + plt.grid() + plt.axis('tight') + plt.show() + + + # shift first hardware signal by 90 degrees to make + # sin signal for mixing, mix and plot + rec3 = make_rec(c, freq1, sin=True) + rec23 = np.multiply(rec2, rec3) + fir_coef, filtered_rec_23 = filter(c, rec23, numtaps, cutoff) + + # plot filtered signal and magnitude and phase results + plt.figure(1) + plt.subplot(411) + plt.title('Re part') + plt.plot(rec12, '-r') + plt.ylabel('unfiltered record', color='r') + plt.plot(filtered_rec_12, '-g') + plt.ylabel('filtered record', color='g') + plt.subplot(412) + plt.title('Im part') + plt.plot(rec23, '-r') + plt.ylabel('unfiltered record', color='r') + plt.plot(filtered_rec_23, '-g') + plt.ylabel('filtered record', color='g') + + # add to plot magnitude and phase of resulting complex number + complex_rec = filtered_rec_12+filtered_rec_23*1j + magnitude = abs(complex_rec) + phase = np.angle(complex_rec, deg=True) + plt.subplot(413) + plt.title('Magnitude') + plt.plot(magnitude) + plt.ylabel('magnitude') + plt.subplot(414) + plt.title('Phase') + plt.plot(phase) + plt.ylabel('phase') + plt.show() + diff --git a/qcodes/instrument_drivers/AlazarTech/filter2_test_controller.py b/qcodes/instrument_drivers/AlazarTech/filter2_test_controller.py new file mode 100644 index 000000000000..7d0838f16579 --- /dev/null +++ b/qcodes/instrument_drivers/AlazarTech/filter2_test_controller.py @@ -0,0 +1,126 @@ +import numpy as np +import matplotlib.pyplot as plt +from scipy import signal + +# two channel filter controller +class Controller(): + def __init__(self, dif_freq): + self.dif_freq = dif_freq + self.sample_rate = 50000000 + self.samples_per_record = 10024 + + record_duration = self.samples_per_record / self.sample_rate + time_period_dif = 1 / self.dif_freq + cycles_measured = record_duration / time_period_dif + oversampling_rate = self.sample_rate / (2 * self.dif_freq) + print("Oscillations per record: {:.2f} (expect 100+)".format(cycles_measured)) + print("Oversampling rate: {:.2f} (expect > 2)".format(oversampling_rate)) + + def pre_start_capture(self): + self.integer_list = np.arange(self.samples_per_record) + +def filter(controller, rec, numtaps, cutoff): + sample_rate = controller.sample_rate + nyq_rate = sample_rate / 2. + fir_coef = signal.firwin(numtaps, cutoff/nyq_rate) + filtered_rec = 2 * signal.lfilter(fir_coef, 1.0, rec) + return fir_coef, filtered_rec + +def filterls(controller, rec, numtaps, cutoff): + sample_rate = controller.sample_rate + nyq_rate = sample_rate / 2. + bands = [0, cutoff/nyq_rate, cutoff/nyq_rate, 1] + desired = [1, 1, 0, 0] + fir_coef = signal.firls(numtaps, bands, desired, nyq=nyq_rate) + filtered_rec = 2 * signal.lfilter(fir_coef, 1.0, rec) + return fir_coef, filtered_rec + +def make_rec(controller, freq, phase_deg=0, ampdif=1, sin=False): + phase_rad = phase_deg*2*np.pi/360 + angle_list = (2 * np.pi * freq * controller.integer_list / controller.sample_rate)+phase_rad + rec = ampdif*np.cos(angle_list) + if sin: + rec = ampdif*np.sin(angle_list) + return rec + + +def main(freq1, freq2, phasedif, ampdif, numtaps, cutoff): + # make an acquisition controller (and sampling rate warnings related to + # given freq1) and integer list used for signal making + c = Controller(freq1) + c.pre_start_capture() + + # make two hardware signals with given frequencies and phase difference + # plot first 100 points of each + rec1 = make_rec(c, freq1) + rec2 = make_rec(c, freq2, phase_deg=phasedif, ampdif=ampdif) + plt.figure(1) + plt.subplot(211) + plt.plot(rec1[:100]) + plt.subplot(212) + plt.plot(rec2[:100]) + plt.show() + + # mix hardware signals and filter out frequencies above cutoff + # should leave only freq_dif component + rec12 = np.multiply(rec1, rec2) + fir_coef, filtered_rec_12 = filterls(c, rec12, numtaps, cutoff) + delay = numtaps-1 + + # plot filter + w, h = signal.freqz(fir_coef) + freq_axis = (w/np.pi)*c.sample_rate + fig = plt.figure(1) + ax1 = fig.add_subplot(111) + plt.title('Digital filter frequency response') + plt.plot(freq_axis, 20 * np.log10(abs(h)), 'b') + plt.ylabel('Amplitude [dB]', color='b') + plt.xlabel('Frequency [Hz]') + + ax2 = ax1.twinx() + angles = np.unwrap(np.angle(h)) + plt.plot(freq_axis, angles, 'g') + plt.ylabel('Angle (radians)', color='g') + plt.grid() + plt.axis('tight') + plt.show() + + + # shift first hardware signal by 90 degrees to make + # sin signal for mixing, mix and plot + rec3 = make_rec(c, freq1, sin=True) + rec23 = np.multiply(rec2, rec3) + fir_coef, filtered_rec_23 = filterls(c, rec23, numtaps, cutoff) + + # plot filtered signal and magnitude and phase results + plt.figure(1) + plt.subplot(411) + plt.title('Re part') + plt.plot(rec12, '-r') + plt.ylabel('unfiltered record', color='r') + plt.plot(filtered_rec_12, '-g') + plt.ylabel('filtered record', color='g') + plt.subplot(412) + plt.title('Im part') + plt.plot(rec23, '-r') + plt.ylabel('unfiltered record', color='r') + plt.plot(filtered_rec_23, '-g') + plt.ylabel('filtered record', color='g') + + # add to plot magnitude and phase of resulting complex number + complex_rec = filtered_rec_12+filtered_rec_23*1j + magnitude = abs(complex_rec) + phase = np.angle(complex_rec, deg=True) + plt.subplot(413) + plt.title('Magnitude') + plt.plot(magnitude) + plt.ylabel('magnitude') + plt.subplot(414) + plt.title('Phase') + plt.plot(phase) + plt.ylabel('phase') + plt.show() + + + + # import qcodes.instrument_drivers.AlazarTech.filter2_test_controller as cont diff --git a/qcodes/instrument_drivers/AlazarTech/local_test.py b/qcodes/instrument_drivers/AlazarTech/local_test.py deleted file mode 100644 index 6e85961fe531..000000000000 --- a/qcodes/instrument_drivers/AlazarTech/local_test.py +++ /dev/null @@ -1,109 +0,0 @@ -from .ATS import AcquisitionController -import math -import numpy as np -import matplotlib.pyplot as plt - - -class Controller(): - def __init__(self, dif_freq): - self.dif_freq = dif_freq - self.sample_rate = 500000000 - self.samples_per_record = 1024 - - record_duration = self.samples_per_record/self.sample_rate - dif_time_period = 1/self.dif_freq - cycles_measured = record_duration / dif_time_period - oversampling_rate = self.sample_rate/(2*self.dif_freq) - print("Measuring from "+str(cycles_measured)+" samples should be on the order of 100 at least") - print("Oversampling rate is "+str(oversampling_rate)+" should be > 2") - - def pre_start_capture(self): - self.integer_list = np.arange(self.samples_per_record) - self.angle_list = (2 * np.pi * self.dif_freq / self.sample_rate * - self.integer_list) - - self.cos_list = np.cos(self.angle_list) - self.sin_list = np.sin(self.angle_list) - - def fit(self, rec): - # Discrete Fourier Transform - RePart = np.dot(rec, self.cos_list) / self.samples_per_record - ImPart = np.dot(rec, self.sin_list) / self.samples_per_record - # factor of 2 is because amplitude is split between finite term and - # double frequency term which averages to 0 - ampl = 2 * np.sqrt(RePart ** 2 + ImPart ** 2) - phase = math.atan2(ImPart, RePart) * 180 / (2 * math.pi) - - return [ampl, phase] - - def pre_acquire(self, alazar): - # gets called after 'AlazarStartCapture' - pass - - def handle_buffer(self, alazar, data): - self.buffer += data - - def post_acquire(self, alazar): - # average over records in buffer: - # for ATS9360 samples are arranged in the buffer as follows: - # S0A, S0B, ..., S1A, S1B, ... - # with SXY the sample number X of channel Y. - records_per_acquisition = (1. * self.buffers_per_acquisition * - self.records_per_buffer) - recordA = np.zeros(self.samples_per_record) - for i in range(self.records_per_buffer): - i0 = i * self.samples_per_record * self.number_of_channels - i1 = i0 + self.samples_per_record * self.number_of_channels - recordA += self.buffer[i0:i1:self.number_of_channels] / records_per_acquisition - - recordB = np.zeros(self.samples_per_record) - for i in range(self.records_per_buffer): - i0 = (i * self.samples_per_record * self.number_of_channels) + 1 - i1 = i0 + self.samples_per_record *self.number_of_channels - recordB += self.buffer[i0:i1:self.number_of_channels] / records_per_acquisition - - resA = self.fit(recordA) - resB = self.fit(recordB) - - return resA, resB - - -def make_rec(controller, freq, phase_deg): - phase_rad = phase_deg*2*np.pi/180 - angle_list = (2 * np.pi * freq / controller.sample_rate * - controller.integer_list)+phase_rad - rec = np.cos(angle_list) - return rec - -def main(freq1, phase): - c = Controller(freq1) - c.pre_start_capture() - ran = 100 - l = [[],[]] - for i in range(ran+1): - freq = freq1-0.002*freq1*(ran/2-i) - rec = make_rec(c, freq, phase) - mag, phase = c.fit(rec) - l[0].append(mag) - l[1].append(phase) - plt.figure(1) - plt.subplot(211) - plt.plot(l[0]) - plt.subplot(212) - plt.plot(l[1]) - plt.show() - # cos_rec = np.multiply(c.cos_list, rec) - # sin_rec = np.multiply(c.sin_list, rec) - #print(c.fit(rec)) - # plt.figure(1) - # plt.subplot(511) - # plt.plot(c.cos_list) - # plt.subplot(512) - # plt.plot(c.sin_list) - # plt.subplot(513) - # plt.plot(rec) - # plt.subplot(514) - # plt.plot(cos_rec) - # plt.subplot(515) - # plt.plot(sin_rec) - # plt.show() diff --git a/qcodes/instrument_drivers/AlazarTech/local_test2.py b/qcodes/instrument_drivers/AlazarTech/local_test2.py deleted file mode 100644 index 9ce6f502e193..000000000000 --- a/qcodes/instrument_drivers/AlazarTech/local_test2.py +++ /dev/null @@ -1,93 +0,0 @@ -from .ATS import AcquisitionController -import math -import numpy as np -import matplotlib.pyplot as plt -from scipy import signal - - -class Controller(): - def __init__(self, dif_freq): - self.dif_freq = dif_freq - self.sample_rate = 5000000 - self.samples_per_record = 10024 - - record_duration = self.samples_per_record/self.sample_rate - dif_time_period = 1/self.dif_freq - cycles_measured = record_duration / dif_time_period - oversampling_rate = self.sample_rate/(2*self.dif_freq) - print("Measuring from "+str(cycles_measured)+" samples should be on the order of 100 at least") - print("Oversampling rate is "+str(oversampling_rate)+" should be > 2") - - def pre_start_capture(self): - self.integer_list = np.arange(self.samples_per_record) - self.angle_list = (2 * np.pi * self.dif_freq / self.sample_rate * - self.integer_list) - - self.cos_list = np.cos(self.angle_list) - self.sin_list = np.sin(self.angle_list) - - def pre_acquire(self, alazar): - # gets called after 'AlazarStartCapture' - pass - - def handle_buffer(self, alazar, data): - self.buffer += data - - def post_acquire(self, alazar): - # average over records in buffer: - # for ATS9360 samples are arranged in the buffer as follows: - # S0A, S0B, ..., S1A, S1B, ... - # with SXY the sample number X of channel Y. - records_per_acquisition = (1. * self.buffers_per_acquisition * - self.records_per_buffer) - recordA = np.zeros(self.samples_per_record) - for i in range(self.records_per_buffer): - i0 = i * self.samples_per_record * self.number_of_channels - i1 = i0 + self.samples_per_record * self.number_of_channels - recordA += self.buffer[i0:i1:self.number_of_channels] / records_per_acquisition - - recordB = np.zeros(self.samples_per_record) - for i in range(self.records_per_buffer): - i0 = (i * self.samples_per_record * self.number_of_channels) + 1 - i1 = i0 + self.samples_per_record *self.number_of_channels - recordB += self.buffer[i0:i1:self.number_of_channels] / records_per_acquisition - - resA = self.fit(recordA) - resB = self.fit(recordB) - - return resA, resB - -def make_rec(controller, freq, phase_deg): - phase_rad = phase_deg*2*np.pi/180 - angle_list = (2 * np.pi * freq / controller.sample_rate * - controller.integer_list)+phase_rad - rec = np.cos(angle_list) - return rec - -def main(freq1, freq2): - c = Controller(freq1) - c.pre_start_capture() - rec1 = make_rec(c, freq2, 0) - rec2 = c.cos_list - plt.figure(1) - plt.subplot(211) - plt.plot(rec1) - plt.subplot(212) - plt.plot(rec1) - plt.show() - s = np.multiply(rec1, rec2) - plt.plot(s) - plt.show() - - -n = 61 -a = signal.firwin(n, cutoff = 0.3, window = "hamming") -#Frequency and phase response -mfreqz(a) -show() -#Impulse and step response -figure(2) -impz(a) -show() - - # import qcodes.instrument_drivers.AlazarTech.local_test2 as cont From e4dcdfdf5a872d498a868e156a37a0c2ccbd6cb9 Mon Sep 17 00:00:00 2001 From: nataliejpg Date: Tue, 25 Oct 2016 18:01:25 +0200 Subject: [PATCH 08/17] fix: ATS closer to working for 9360 --- qcodes/instrument_drivers/AlazarTech/ATS.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qcodes/instrument_drivers/AlazarTech/ATS.py b/qcodes/instrument_drivers/AlazarTech/ATS.py index a933c18a4e8d..5cfa7fb5c7cc 100644 --- a/qcodes/instrument_drivers/AlazarTech/ATS.py +++ b/qcodes/instrument_drivers/AlazarTech/ATS.py @@ -614,13 +614,13 @@ def acquire(self, mode=None, samples_per_record=None, print("completed AlazarPostAsyncBuffer") # -----start capture here----- - acquisition_controller.pre_start_capture(self) + acquisition_controller.pre_start_capture() start = time.clock() # Keep track of when acquisition started # call the startcapture method self._call_dll('AlazarStartCapture', self._handle) print("Capturing %d buffers." % buffers_per_acquisition) - acquisition_controller.pre_acquire(self) + acquisition_controller.pre_acquire() # buffer handling from acquisition buffers_completed = 0 From 1cf0080620ce06f212cc4d9d619a4a638fabad81 Mon Sep 17 00:00:00 2001 From: nataliejpg Date: Thu, 27 Oct 2016 15:13:21 +0200 Subject: [PATCH 09/17] feature: basic yoko driver added --- qcodes/instrument_drivers/yokogawa/GS200.py | 28 +++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 qcodes/instrument_drivers/yokogawa/GS200.py diff --git a/qcodes/instrument_drivers/yokogawa/GS200.py b/qcodes/instrument_drivers/yokogawa/GS200.py new file mode 100644 index 000000000000..fecf42751548 --- /dev/null +++ b/qcodes/instrument_drivers/yokogawa/GS200.py @@ -0,0 +1,28 @@ +from qcodes.instrument.visa import VisaInstrument +from qcodes.utils.validators import Numbers + + +class GS200(VisaInstrument): + + def __init__(self, name, address, reset=False, **kwargs): + super().__init__(name, address, **kwargs) + + # Add parameters to wrapper + self.add_parameter('voltage', + label='Voltage', + units='V', + get_cmd=':SOURce:LEVel?', + set_cmd=':SOURce:LEVel:AUTO {:.4f}', + get_parser=float, + vals=Numbers(-10, 10)) + + self.initialise() + self.connect_message() + + def initialise(self): + self.write('*RST') + self.write(':SYST:DISP ON') + self.write(':SOUR:FUNC VOLT') + self.write(':SOUR:PROT:CURR MIN') + self.write(':OUTP:STAT ON') + self.voltage.set(0) From 4b7b47f502cb8999ecd5f67df96307a9f1d6b82b Mon Sep 17 00:00:00 2001 From: nataliejpg Date: Mon, 31 Oct 2016 14:15:58 +0100 Subject: [PATCH 10/17] comments and small tidy --- qcodes/instrument_drivers/yokogawa/GS200.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/qcodes/instrument_drivers/yokogawa/GS200.py b/qcodes/instrument_drivers/yokogawa/GS200.py index fecf42751548..97a963f0e557 100644 --- a/qcodes/instrument_drivers/yokogawa/GS200.py +++ b/qcodes/instrument_drivers/yokogawa/GS200.py @@ -3,11 +3,19 @@ class GS200(VisaInstrument): + """ + This is the qcodes driver for the Yokogawa GS200 voltage and current source + Usage: Initialize with + = GS200(, address='', reset=) + + + TODO:(nataliejpg) + - add current functionality (mode settings) + """ def __init__(self, name, address, reset=False, **kwargs): super().__init__(name, address, **kwargs) - # Add parameters to wrapper self.add_parameter('voltage', label='Voltage', units='V', @@ -15,12 +23,14 @@ def __init__(self, name, address, reset=False, **kwargs): set_cmd=':SOURce:LEVel:AUTO {:.4f}', get_parser=float, vals=Numbers(-10, 10)) - + + self.add_function('reset', call_cmd='*RST') + self.initialise() self.connect_message() def initialise(self): - self.write('*RST') + self.reset() self.write(':SYST:DISP ON') self.write(':SOUR:FUNC VOLT') self.write(':SOUR:PROT:CURR MIN') From f129daafd9166744189c5991c342e26259f9aefc Mon Sep 17 00:00:00 2001 From: nataliejpg Date: Sat, 19 Nov 2016 20:15:43 +0100 Subject: [PATCH 11/17] remove reset on initialisation --- qcodes/instrument_drivers/yokogawa/GS200.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/qcodes/instrument_drivers/yokogawa/GS200.py b/qcodes/instrument_drivers/yokogawa/GS200.py index 97a963f0e557..683d557394ba 100644 --- a/qcodes/instrument_drivers/yokogawa/GS200.py +++ b/qcodes/instrument_drivers/yokogawa/GS200.py @@ -30,9 +30,7 @@ def __init__(self, name, address, reset=False, **kwargs): self.connect_message() def initialise(self): - self.reset() self.write(':SYST:DISP ON') self.write(':SOUR:FUNC VOLT') self.write(':SOUR:PROT:CURR MIN') self.write(':OUTP:STAT ON') - self.voltage.set(0) From 678f310aa25393b97e9b1729d5992de33892e39e Mon Sep 17 00:00:00 2001 From: Natalie Pearson Date: Sat, 19 Nov 2016 20:18:13 +0100 Subject: [PATCH 12/17] pep8 --- qcodes/instrument_drivers/yokogawa/GS200.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/qcodes/instrument_drivers/yokogawa/GS200.py b/qcodes/instrument_drivers/yokogawa/GS200.py index 683d557394ba..5c63b241caf9 100644 --- a/qcodes/instrument_drivers/yokogawa/GS200.py +++ b/qcodes/instrument_drivers/yokogawa/GS200.py @@ -9,26 +9,27 @@ class GS200(VisaInstrument): Usage: Initialize with = GS200(, address='', reset=) - + TODO:(nataliejpg) - add current functionality (mode settings) """ + def __init__(self, name, address, reset=False, **kwargs): super().__init__(name, address, **kwargs) self.add_parameter('voltage', - label='Voltage', + label='Voltage', units='V', get_cmd=':SOURce:LEVel?', set_cmd=':SOURce:LEVel:AUTO {:.4f}', get_parser=float, vals=Numbers(-10, 10)) - + self.add_function('reset', call_cmd='*RST') - + self.initialise() self.connect_message() - + def initialise(self): self.write(':SYST:DISP ON') self.write(':SOUR:FUNC VOLT') From 71b0b24287360800691fd9d5d9fa970f0290b927 Mon Sep 17 00:00:00 2001 From: Natalie Pearson Date: Sat, 19 Nov 2016 20:27:25 +0100 Subject: [PATCH 13/17] deleting the mess that was me branching off of my ATS9360 dev branch so this one is cleaner --- qcodes/instrument_drivers/AlazarTech/ATS.py | 202 ++++--------- .../instrument_drivers/AlazarTech/ATS9360.py | 283 ------------------ .../AlazarTech/ATS_acquisition_controllers.py | 138 --------- .../AlazarTech/HD_test_controller.py | 105 ------- .../QDev_acquisition_controllers.py | 246 --------------- .../AlazarTech/filter1_test_controller.py | 124 -------- .../AlazarTech/filter2_test_controller.py | 126 -------- 7 files changed, 65 insertions(+), 1159 deletions(-) delete mode 100644 qcodes/instrument_drivers/AlazarTech/ATS9360.py delete mode 100644 qcodes/instrument_drivers/AlazarTech/HD_test_controller.py delete mode 100644 qcodes/instrument_drivers/AlazarTech/QDev_acquisition_controllers.py delete mode 100644 qcodes/instrument_drivers/AlazarTech/filter1_test_controller.py delete mode 100644 qcodes/instrument_drivers/AlazarTech/filter2_test_controller.py diff --git a/qcodes/instrument_drivers/AlazarTech/ATS.py b/qcodes/instrument_drivers/AlazarTech/ATS.py index 5cfa7fb5c7cc..2ea35c71ae43 100644 --- a/qcodes/instrument_drivers/AlazarTech/ATS.py +++ b/qcodes/instrument_drivers/AlazarTech/ATS.py @@ -1,7 +1,6 @@ import ctypes import logging import numpy as np -import time import os from qcodes.instrument.base import Instrument @@ -13,13 +12,12 @@ # these items are important for generalizing this code to multiple alazar cards # TODO(damazter) (W) remove 8 bits per sample requirement # TODO(damazter) (W) some alazar cards have a different number of channels :( +# this driver only works with 2-channel cards # TODO(damazter) (S) tests to do: # acquisition that would overflow the board if measurement is not stopped # quickly enough. can this be solved by not reposting the buffers? -# TODO(nataliejpg) test!! - class AlazarTech_ATS(Instrument): """ @@ -223,15 +221,7 @@ def get_board_info(cls, dll, system_id, board_id): def __init__(self, name, system_id=1, board_id=1, dll_path=None, **kwargs): super().__init__(name, **kwargs) - self._ATS_dll = None - - if os.name == 'nt': - self._ATS_dll = ctypes.cdll.LoadLibrary(dll_path or self.dll_path) - else: - raise Exception("Unsupported OS") - - # TODO (W) make the board id more general such that more than one card - # per system configurations are supported + self._ATS_dll = ctypes.cdll.LoadLibrary(dll_path or self.dll_path) self._handle = self._ATS_dll.AlazarGetBoardBySystemID(system_id, board_id) @@ -308,6 +298,7 @@ def get_idn(self): self._handle, 0x10000031, 0, value.ctypes.data) pcie_link_width = str(value[0]) + return {'firmware': None, 'model': board_kind, 'serial': serial, @@ -399,10 +390,9 @@ def config(self, clock_source=None, sample_rate=None, clock_edge=None, self.parameters['coupling' + str(i)], self.parameters['channel_range' + str(i)], self.parameters['impedance' + str(i)]) - if bwlimit is not None: - self._call_dll('AlazarSetBWLimit', - self._handle, i, - self.parameters['bwlimit' + str(i)]) + self._call_dll('AlazarSetBWLimit', + self._handle, i, + self.parameters['bwlimit' + str(i)]) self._call_dll('AlazarSetTriggerOperation', self._handle, self.trigger_operation, @@ -467,7 +457,7 @@ def acquire(self, mode=None, samples_per_record=None, self._set_if_present('samples_per_record', samples_per_record) self._set_if_present('records_per_buffer', records_per_buffer) self._set_if_present('buffers_per_acquisition', - buffers_per_acquisition), + buffers_per_acquisition) self._set_if_present('channel_selection', channel_selection) self._set_if_present('transfer_offset', transfer_offset) self._set_if_present('external_startcapture', external_startcapture) @@ -491,6 +481,11 @@ def acquire(self, mode=None, samples_per_record=None, # Abort any previous measurement self._call_dll('AlazarAbortAsyncRead', self._handle) + # get channel info + max_s, bps = self._get_channel_info(self._handle) + if bps != 8: + raise Exception('Only 8 bits per sample supported at this moment') + # Set record size for NPT mode if mode == 'NPT': pretriggersize = 0 # pretriggersize is 0 for NPT always @@ -500,6 +495,10 @@ def acquire(self, mode=None, samples_per_record=None, post_trigger_size) # set acquisition parameters here for NPT, TS mode + if self.channel_selection._get_byte() == 3: + number_of_channels = 2 + else: + number_of_channels = 1 samples_per_buffer = 0 buffers_per_acquisition = self.buffers_per_acquisition._get_byte() samples_per_record = self.samples_per_record._get_byte() @@ -513,7 +512,8 @@ def acquire(self, mode=None, samples_per_record=None, if mode == 'NPT': records_per_buffer = self.records_per_buffer._get_byte() - records_per_acquisition = records_per_buffer * buffers_per_acquisition + records_per_acquisition = ( + records_per_buffer * buffers_per_acquisition) samples_per_buffer = samples_per_record * records_per_buffer self._call_dll('AlazarBeforeAsyncRead', @@ -554,37 +554,7 @@ def acquire(self, mode=None, samples_per_record=None, self.interleave_samples._set_updated() self.get_processed_data._set_updated() - # bytes per sample - handle = self._handle - max_s, bps = self._get_channel_info(handle) - bytes_per_sample = (bps + 7) // 8 - print("bytes per sample "+str(bytes_per_sample)) - - # bytes per record - bytes_per_record = bytes_per_sample * samples_per_record - print("samples_per_record is "+str(samples_per_record)) - print("bytes per record is "+str(bytes_per_record)) - - # channels - if self.channel_selection._get_byte() == 3: - number_of_channels = 2 - else: - number_of_channels = 1 - - # bytes per buffer - bytes_per_buffer = bytes_per_record * records_per_buffer * number_of_channels - # create buffers for acquisition - # TODO(nataliejpg) should this be > 1 (as intuitive) or > 8 as in alazar sample code? - sample_type = ctypes.c_uint8 - if bytes_per_sample > 1: - sample_type = ctypes.c_uint16 - - # TODO(nataliejpg) get rid of all of thes print statements - print("samples_per_buffer is "+str(samples_per_buffer)) - print("bytes_per_buffer is "+str(bytes_per_buffer)) - print("records_per_buffer is "+str(records_per_buffer)) - self.clear_buffers() # make sure that allocated_buffers <= buffers_per_acquisition if (self.allocated_buffers._get_byte() > @@ -599,41 +569,33 @@ def acquire(self, mode=None, samples_per_record=None, for k in range(allocated_buffers): try: - self.buffer_list.append(Buffer(sample_type, bytes_per_buffer)) + self.buffer_list.append(Buffer(bps, samples_per_buffer, + number_of_channels)) except: self.clear_buffers() raise # post buffers to Alazar - print("made buffer list length "+str(len(self.buffer_list))) for buf in self.buffer_list: - self._ATS_dll.AlazarPostAsyncBuffer.argtypes = [ctypes.c_uint32, ctypes.c_void_p, ctypes.c_uint32] self._call_dll('AlazarPostAsyncBuffer', self._handle, buf.addr, buf.size_bytes) self.allocated_buffers._set_updated() - print("completed AlazarPostAsyncBuffer") # -----start capture here----- acquisition_controller.pre_start_capture() - start = time.clock() # Keep track of when acquisition started # call the startcapture method self._call_dll('AlazarStartCapture', self._handle) - print("Capturing %d buffers." % buffers_per_acquisition) acquisition_controller.pre_acquire() - # buffer handling from acquisition buffers_completed = 0 - bytes_transferred = 0 buffer_timeout = self.buffer_timeout._get_byte() self.buffer_timeout._set_updated() buffer_recycling = (self.buffers_per_acquisition._get_byte() > self.allocated_buffers._get_byte()) - while (buffers_completed < self.buffers_per_acquisition._get_byte()): - # Wait for the buffer at the head of the list of available - # buffers to be filled by the board. + while buffers_completed < self.buffers_per_acquisition._get_byte(): buf = self.buffer_list[buffers_completed % allocated_buffers] self._call_dll('AlazarWaitAsyncBufferComplete', @@ -646,12 +608,12 @@ def acquire(self, mode=None, samples_per_record=None, # if buffers must be recycled, extract data and repost them # otherwise continue to next buffer + if buffer_recycling: acquisition_controller.handle_buffer(buf.buffer) self._call_dll('AlazarPostAsyncBuffer', self._handle, buf.addr, buf.size_bytes) buffers_completed += 1 - bytes_transferred += buf.size_bytes # stop measurement here self._call_dll('AlazarAbortAsyncRead', self._handle) @@ -672,23 +634,6 @@ def acquire(self, mode=None, samples_per_record=None, # return result return acquisition_controller.post_acquire() - # Compute the total transfer time, and display performance information. - transfer_time_sec = time.clock() - start - print("Capture completed in %f sec" % transfer_time_sec) - buffers_per_sec = 0 - bytes_per_sec = 0 - records_per_sec = 0 - if transfer_time_sec > 0: - buffers_per_sec = buffers_completed / transfer_time_sec - bytes_per_sec = bytes_transferred / transfer_time_sec - records_per_sec = records_per_buffer * buffers_completed / transfer_time_sec - print("Captured %d buffers (%f buffers per sec)" % - (buffers_completed, buffers_per_sec)) - print("Captured %d records (%f records per sec)" % - (records_per_buffer * buffers_completed, records_per_sec)) - print("Transferred %d bytes (%f bytes per sec)" % - (bytes_transferred, bytes_per_sec)) - def _set_if_present(self, param_name, value): if value is not None: self.parameters[param_name]._set(value) @@ -713,7 +658,7 @@ def _call_dll(self, func_name, *args): args_out = [] update_params = [] for arg in args: - if isinstance(arg, AlazarParameter): + if isinstance(arg,AlazarParameter): args_out.append(arg._get_byte()) update_params.append(arg) else: @@ -721,14 +666,10 @@ def _call_dll(self, func_name, *args): # run the function func = getattr(self._ATS_dll, func_name) - try: - return_code = func(*args_out) - except Exception as e: - print(e) - raise + return_code = func(*args_out) # check for errors - if (return_code != self._success) and (return_code != 518): + if (return_code != self._success) and (return_code !=518): # TODO(damazter) (C) log error argrepr = repr(args_out) @@ -757,7 +698,6 @@ def clear_buffers(self): """ for b in self.buffer_list: b.free_mem() - print("buffers cleared") self.buffer_list = [] def signal_to_volt(self, channel, signal): @@ -895,56 +835,44 @@ def _set_updated(self): class Buffer: - """Buffer suitable for DMA transfers. - - AlazarTech digitizers use direct memory access (DMA) to transfer - data from digitizers to the computer's main memory. This class - abstracts a memory buffer on the host, and ensures that all the - requirements for DMA transfers are met. - - Buffer export a 'buffer' member, which is a NumPy array view - of the underlying memory buffer + """ + This class represents a single buffer used for the data acquisition Args: - c_sample_type (ctypes type): The datatype of the buffer to create. - size_bytes (int): The size of the buffer to allocate, in bytes. + bits_per_sample: the number of bits needed to store a sample + samples_per_buffer: the number of samples needed per buffer(per channel) + number_of_channels: the number of channels that will be stored in the + buffer """ - def __init__(self, c_sample_type, size_bytes): - self.size_bytes = size_bytes - - npSampleType = { - ctypes.c_uint8: np.uint8, - ctypes.c_uint16: np.uint16, - ctypes.c_uint32: np.uint32, - ctypes.c_int32: np.int32, - ctypes.c_float: np.float32 - }.get(c_sample_type, 0) - - bytes_per_sample = { - ctypes.c_uint8: 1, - ctypes.c_uint16: 2, - ctypes.c_uint32: 4, - ctypes.c_int32: 4, - ctypes.c_float: 4 - }.get(c_sample_type, 0) - + def __init__(self, bits_per_sample, samples_per_buffer, + number_of_channels): + if bits_per_sample != 8: + raise Exception("Buffer: only 8 bit per sample supported") + if os.name != 'nt': + raise Exception("Buffer: only Windows supported at this moment") self._allocated = True - self.addr = None - if os.name == 'nt': - MEM_COMMIT = 0x1000 - PAGE_READWRITE = 0x4 - ctypes.windll.kernel32.VirtualAlloc.argtypes = [ctypes.c_void_p, ctypes.c_long, ctypes.c_long, ctypes.c_long] - ctypes.windll.kernel32.VirtualAlloc.restype = ctypes.c_void_p - self.addr = ctypes.windll.kernel32.VirtualAlloc( - 0, ctypes.c_long(size_bytes), MEM_COMMIT, PAGE_READWRITE) - else: + + # try to allocate memory + mem_commit = 0x1000 + page_readwrite = 0x4 + + self.size_bytes = samples_per_buffer * number_of_channels + + # for documentation please see: + # https://msdn.microsoft.com/en-us/library/windows/desktop/aa366887(v=vs.85).aspx + ctypes.windll.kernel32.VirtualAlloc.argtypes = [ + ctypes.c_void_p, ctypes.c_long, ctypes.c_long, ctypes.c_long] + ctypes.windll.kernel32.VirtualAlloc.restype = ctypes.c_void_p + self.addr = ctypes.windll.kernel32.VirtualAlloc( + 0, ctypes.c_long(self.size_bytes), mem_commit, page_readwrite) + if self.addr is None: self._allocated = False - raise Exception("Unsupported OS") + e = ctypes.windll.kernel32.GetLastError() + raise Exception("Memory allocation error: " + str(e)) - ctypes_array = (c_sample_type * - (size_bytes // bytes_per_sample)).from_address(self.addr) - self.buffer = np.frombuffer(ctypes_array, dtype=npSampleType) - self.ctypes_buffer = ctypes_array + ctypes_array = (ctypes.c_uint8 * + self.size_bytes).from_address(self.addr) + self.buffer = np.frombuffer(ctypes_array, dtype=np.uint8) pointer, read_only_flag = self.buffer.__array_interface__['data'] def free_mem(self): @@ -952,16 +880,16 @@ def free_mem(self): uncommit memory allocated with this buffer object :return: None """ + mem_release = 0x8000 + # for documentation please see: + # https://msdn.microsoft.com/en-us/library/windows/desktop/aa366892(v=vs.85).aspx + ctypes.windll.kernel32.VirtualFree.argtypes = [ + ctypes.c_void_p, ctypes.c_long, ctypes.c_long] + ctypes.windll.kernel32.VirtualFree.restype = ctypes.c_int + ctypes.windll.kernel32.VirtualFree(ctypes.c_void_p(self.addr), 0, + mem_release) self._allocated = False - if os.name == 'nt': - MEM_RELEASE = 0x8000 - ctypes.windll.kernel32.VirtualFree.argtypes = [ctypes.c_void_p, ctypes.c_long, ctypes.c_long] - ctypes.windll.kernel32.VirtualFree.restype = ctypes.c_int - ctypes.windll.kernel32.VirtualFree(ctypes.c_void_p(self.addr), 0, MEM_RELEASE); - else: - self._allocated = True - raise Exception("Unsupported OS") def __del__(self): """ diff --git a/qcodes/instrument_drivers/AlazarTech/ATS9360.py b/qcodes/instrument_drivers/AlazarTech/ATS9360.py deleted file mode 100644 index a564c01a9fbb..000000000000 --- a/qcodes/instrument_drivers/AlazarTech/ATS9360.py +++ /dev/null @@ -1,283 +0,0 @@ -from .ATS import AlazarTech_ATS, AlazarParameter -from qcodes.utils import validators - - -class AlazarTech_ATS9360(AlazarTech_ATS): - def __init__(self, name, server_name=None): - dll_path = 'C:\\WINDOWS\\System32\\ATSApi.dll' - super().__init__(name, dll_path=dll_path) - - # add parameters - - # ----- Parameters for the configuration of the board ----- - self.add_parameter(name='clock_source', - parameter_class=AlazarParameter, - label='Clock Source', - unit=None, - value='INTERNAL_CLOCK', - byte_to_value_dict={1: 'INTERNAL_CLOCK', - 4: 'SLOW_EXTERNAL_CLOCK', - 5: 'EXTERNAL_CLOCK_AC', - 7: 'EXTERNAL_CLOCK_10_MHz_REF'}) - self.add_parameter(name='sample_rate', - parameter_class=AlazarParameter, - label='Sample Rate', - unit='S/s', - value=500000000, - byte_to_value_dict={ - 0x1: 1000, 0x2: 2000, 0x4: 5000, 0x8: 10000, - 0xA: 20000, 0xC: 50000, 0xE: 100000, - 0x10: 200000, 0x12: 500000, 0x14: 1000000, - 0x18: 2000000, 0x1A: 5000000, 0x1C: 10000000, - 0x1E: 20000000, 0x22: 50000000, 0x24: 100000000, - 0x2B: 250000000, 0x30: 500000000, - 0x35: 1000000000, 0x40: 'EXTERNAL_CLOCK', - 1000000000: '1GHz_REFERENCE_CLOCK'}) - self.add_parameter(name='clock_edge', - parameter_class=AlazarParameter, - label='Clock Edge', - unit=None, - value='CLOCK_EDGE_RISING', - byte_to_value_dict={0: 'CLOCK_EDGE_RISING', - 1: 'CLOCK_EDGE_FALLING'}) - - self.add_parameter(name='decimation', - parameter_class=AlazarParameter, - label='Decimation', - unit=None, - value=0, - vals=validators.Ints(0, 100000)) - - for i in ['1', '2']: - self.add_parameter(name='coupling' + i, - parameter_class=AlazarParameter, - label='Coupling channel ' + i, - unit=None, - value='DC', - byte_to_value_dict={1: 'AC', 2: 'DC'}) - self.add_parameter(name='channel_range' + i, - parameter_class=AlazarParameter, - label='Range channel ' + i, - unit='V', - value=0.4, - byte_to_value_dict={ - 2: 0.04, 5: 0.1, 6: 0.2, 7: 0.4, - 10: 1., 11: 2., 12: 4.}) - self.add_parameter(name='impedance' + i, - parameter_class=AlazarParameter, - label='Impedance channel ' + i, - unit='Ohm', - value=50, - byte_to_value_dict={1: 1000000, 2: 50}) - - self.add_parameter(name='trigger_operation', - parameter_class=AlazarParameter, - label='Trigger Operation', - unit=None, - value='TRIG_ENGINE_OP_J', - byte_to_value_dict={ - 0: 'TRIG_ENGINE_OP_J', - 1: 'TRIG_ENGINE_OP_K', - 2: 'TRIG_ENGINE_OP_J_OR_K', - 3: 'TRIG_ENGINE_OP_J_AND_K', - 4: 'TRIG_ENGINE_OP_J_XOR_K', - 5: 'TRIG_ENGINE_OP_J_AND_NOT_K', - 6: 'TRIG_ENGINE_OP_NOT_J_AND_K'}) - for i in ['1', '2']: - self.add_parameter(name='trigger_engine' + i, - parameter_class=AlazarParameter, - label='Trigger Engine ' + i, - unit=None, - value='TRIG_ENGINE_J', - byte_to_value_dict={0: 'TRIG_ENGINE_J', - 1: 'TRIG_ENGINE_K'}) - self.add_parameter(name='trigger_source' + i, - parameter_class=AlazarParameter, - label='Trigger Source ' + i, - unit=None, - value='DISABLE', - byte_to_value_dict={0: 'CHANNEL_A', - 1: 'CHANNEL_B', - 2: 'EXTERNAL', - 3: 'DISABLE'}) - self.add_parameter(name='trigger_slope' + i, - parameter_class=AlazarParameter, - label='Trigger Slope ' + i, - unit=None, - value='TRIG_SLOPE_POSITIVE', - byte_to_value_dict={1: 'TRIG_SLOPE_POSITIVE', - 2: 'TRIG_SLOPE_NEGATIVE'}) - self.add_parameter(name='trigger_level' + i, - parameter_class=AlazarParameter, - label='Trigger Level ' + i, - unit=None, - value=150, - vals=validators.Ints(0, 255)) - - self.add_parameter(name='external_trigger_coupling', - parameter_class=AlazarParameter, - label='External Trigger Coupling', - unit=None, - value='AC', - byte_to_value_dict={1: 'AC', 2: 'DC'}) - self.add_parameter(name='external_trigger_range', - parameter_class=AlazarParameter, - label='External Trigger Range', - unit=None, - value='ETR_5V', - byte_to_value_dict={0: 'ETR_5V', 1: 'ETR_1V'}) - self.add_parameter(name='trigger_delay', - parameter_class=AlazarParameter, - label='Trigger Delay', - unit='Sample clock cycles', - value=0, - vals=validators.Ints(min_value=0)) - - # NOTE: The board will wait for a for this amount of time for a - # trigger event. If a trigger event does not arrive, then the - # board will automatically trigger. Set the trigger timeout value - # to 0 to force the board to wait forever for a trigger event. - # - # IMPORTANT: The trigger timeout value should be set to zero after - # appropriate trigger parameters have been determined, otherwise - # the board may trigger if the timeout interval expires before a - # hardware trigger event arrives. - self.add_parameter(name='timeout_ticks', - parameter_class=AlazarParameter, - label='Timeout Ticks', - unit='10 us', - value=0, - vals=validators.Ints(min_value=0)) - - # ----- Parameters for the acquire function ----- - self.add_parameter(name='mode', - parameter_class=AlazarParameter, - label='Acquisiton mode', - unit=None, - value='NPT', - byte_to_value_dict={0x200: 'NPT', 0x400: 'TS'}) - - # samples_per_record must be a multiple of 16! - self.add_parameter(name='samples_per_record', - parameter_class=AlazarParameter, - label='Samples per Record', - unit=None, - value=1024, - vals=Multiples(divisor=16, min_value=0)) - # TODO (M) figure out if this also has to be a multiple of something, - # I could not find this in the documentation but somehow I have the - # feeling it still should be a multiple of something - # NOTE by ramiro: At least in previous python implementations(PycQED delft), - # this is an artifact for compatibility with AWG sequencing, - # not particular to any ATS architecture. - # ==> this is a construction imposed by the memory strategy - # implemented on the python driver - # we are writing, not limited by any actual ATS feature. - - self.add_parameter(name='records_per_buffer', - parameter_class=AlazarParameter, - label='Records per Buffer', - unit=None, - value=10, - vals=validators.Ints(min_value=0)) - self.add_parameter(name='buffers_per_acquisition', - parameter_class=AlazarParameter, - label='Buffers per Acquisition', - unit=None, - value=10, - vals=validators.Ints(min_value=0)) - self.add_parameter(name='channel_selection', - parameter_class=AlazarParameter, - label='Channel Selection', - unit=None, - value='AB', - byte_to_value_dict={1: 'A', 2: 'B', 3: 'AB'}) - self.add_parameter(name='transfer_offset', - parameter_class=AlazarParameter, - label='Transer Offset', - unit='Samples', - value=0, - vals=validators.Ints(min_value=0)) - self.add_parameter(name='external_startcapture', - parameter_class=AlazarParameter, - label='External Startcapture', - unit=None, - value='ENABLED', - byte_to_value_dict={0x0: 'DISABLED', - 0x1: 'ENABLED'}) - self.add_parameter(name='enable_record_headers', - parameter_class=AlazarParameter, - label='Enable Record Headers', - unit=None, - value='DISABLED', - byte_to_value_dict={0x0: 'DISABLED', - 0x8: 'ENABLED'}) - self.add_parameter(name='alloc_buffers', - parameter_class=AlazarParameter, - label='Alloc Buffers', - unit=None, - value='DISABLED', - byte_to_value_dict={0x0: 'DISABLED', - 0x20: 'ENABLED'}) - self.add_parameter(name='fifo_only_streaming', - parameter_class=AlazarParameter, - label='Fifo Only Streaming', - unit=None, - value='DISABLED', - byte_to_value_dict={0x0: 'DISABLED', - 0x800: 'ENABLED'}) - self.add_parameter(name='interleave_samples', - parameter_class=AlazarParameter, - label='Interleave Samples', - unit=None, - value='DISABLED', - byte_to_value_dict={0x0: 'DISABLED', - 0x1000: 'ENABLED'}) - self.add_parameter(name='get_processed_data', - parameter_class=AlazarParameter, - label='Get Processed Data', - unit=None, - value='DISABLED', - byte_to_value_dict={0x0: 'DISABLED', - 0x2000: 'ENABLED'}) - - self.add_parameter(name='allocated_buffers', - parameter_class=AlazarParameter, - label='Allocated Buffers', - unit=None, - value=2, - vals=validators.Ints(min_value=0)) - self.add_parameter(name='buffer_timeout', - parameter_class=AlazarParameter, - label='Buffer Timeout', - unit='ms', - value=1000, - vals=validators.Ints(min_value=0)) - - # TODO (M) make parameter for board type - - # TODO (M) check board kind - - -class Multiples(validators.Ints): - ''' - requires an integer - optional parameters min_value and max_value enforce - min_value <= value <= max_value - divisor enforces that value % divisor == 0 - ''' - - def __init__(self, divisor=1, **kwargs): - super().__init__(**kwargs) - if not isinstance(divisor, int): - raise TypeError('divisor must be an integer') - self._divisor = divisor - - def validate(self, value, context=''): - super().validate(value=value, context=context) - if not value % self._divisor == 0: - raise TypeError('{} is not a multiple of {}; {}'.format( - repr(value), repr(self._divisor), context)) - - def __repr__(self): - return super().__repr__()[:-1] + ', Multiples of {}>'.format(self._divisor) diff --git a/qcodes/instrument_drivers/AlazarTech/ATS_acquisition_controllers.py b/qcodes/instrument_drivers/AlazarTech/ATS_acquisition_controllers.py index f4653cf1776d..919e777cab43 100644 --- a/qcodes/instrument_drivers/AlazarTech/ATS_acquisition_controllers.py +++ b/qcodes/instrument_drivers/AlazarTech/ATS_acquisition_controllers.py @@ -3,142 +3,6 @@ import numpy as np -class HD_Controller(AcquisitionController): - """Heterodyne Measurement Controller - Does averaged DFT on 2 channel Alazar measurement - - TODO(nataliejpg) handling of channel number - TODO(nataliejpg) test angle data - """ - - def __init__(self, freq_dif): - self.freq_dif = freq_dif - self.samples_per_record = None - self.records_per_buffer = None - self.buffers_per_acquisition = None - self.number_of_channels = 2 - self.buffer = None - - def pre_start_capture(self, alazar): - """Get config data from alazar card and set up DFT""" - self.samples_per_record = alazar.samples_per_record() - self.records_per_buffer = alazar.records_per_buffer() - self.buffers_per_acquisition = alazar.buffers_per_acquisition() - self.sample_rate = alazar.get_sample_speed() - self.buffer = np.zeros(self.samples_per_record * - self.records_per_buffer * - self.number_of_channels) - # TODO(nataliejpg) leave explicit or save lines? add error/logging? - averaging = self.buffers_per_acquisition * self.records_per_buffer - record_duration = self.samples_per_record / self.sample_rate - time_period_dif = 1 / self.freq_dif - cycles_measured = record_duration / time_period_dif - oversampling_rate = self.sample_rate / (2 * self.freq_dif) - print("Average over {} records".format(averaging)) - print("Oscillations per record: {} (expect 100+)" - .format(cycles_measured)) - print("Oversampling rate: {} (expect > 2)".format(oversampling_rate)) - - integer_list = np.arange(self.samples_per_record) - angle_list = (2 * np.pi * self.freq_dif / self.sample_rate * - integer_list) - self.cos_list = np.cos(angle_list) - self.sin_list = np.sin(angle_list) - - def pre_acquire(self, alazar): - # gets called after 'AlazarStartCapture' - pass - - def handle_buffer(self, alazar, data): - self.buffer += data - - def post_acquire(self, alazar): - """Average over records in buffer and do DFT: - assumes samples are arranged in the buffer as follows: - S0A, S0B, ..., S1A, S1B, ... - with SXY the sample number X of channel Y. - """ - records_per_acquisition = (1. * self.buffers_per_acquisition * - self.records_per_buffer) - recordA = np.zeros(self.samples_per_record) - for i in range(self.records_per_buffer): - i0 = (i * self.samples_per_record * self.number_of_channels) - i1 = (i0 + self.samples_per_record * self.number_of_channels) - recordA += (self.buffer[i0:i1:self.number_of_channels] / - records_per_acquisition) - recordB = np.zeros(self.samples_per_record) - for i in range(self.records_per_buffer): - i0 = (i * self.samples_per_record * self.number_of_channels + 1) - i1 = (i0 + self.samples_per_record * self.number_of_channels) - recordB += (self.buffer[i0:i1:self.number_of_channels] / - records_per_acquisition) - - resA = self.fit(recordA) - resB = self.fit(recordB) - - return resA, resB - - def fit(self, rec): - """Do Discrete Fourier Transform and return magnitude and phase data""" - RePart = np.dot(rec, self.cos_list) / self.samples_per_record - ImPart = np.dot(rec, self.sin_list) / self.samples_per_record - # factor of 2 as amplitude is split between finite term - # and double frequency term - ampl = 2 * np.sqrt(RePart ** 2 + ImPart ** 2) - phase = math.atan2(ImPart, RePart) * 180 / (2 * math.pi) - return [ampl, phase] - - -class Basic_AcquisitionController(AcquisitionController): - """Basic AcquisitionController tested on ATS9360 - returns unprocessed data averaged by record with 2 channels - """ - - def __init__(self): - self.samples_per_record = None - self.records_per_buffer = None - self.buffers_per_acquisition = None - self.number_of_channels = 2 - self.buffer = None - - def pre_start_capture(self, alazar): - self.samples_per_record = alazar.samples_per_record() - self.records_per_buffer = alazar.records_per_buffer() - self.buffers_per_acquisition = alazar.buffers_per_acquisition() - self.buffer = np.zeros(self.samples_per_record * - self.records_per_buffer * - self.number_of_channels) - - def pre_acquire(self, alazar): - # gets called after 'AlazarStartCapture' - pass - - def handle_buffer(self, alazar, data): - self.buffer += data - - def post_acquire(self, alazar): - # average over records in buffer: - # for ATS9360 samples are arranged in the buffer as follows: - # S0A, S0B, ..., S1A, S1B, ... - # with SXY the sample number X of channel Y. - records_per_acquisition = (self.buffers_per_acquisition * - self.records_per_buffer) - recordA = np.zeros(self.samples_per_record) - for i in range(self.records_per_buffer): - i0 = (i * self.samples_per_record * self.number_of_channels) - i1 = (i0 + self.samples_per_record * self.number_of_channels) - recordA += (self.buffer[i0:i1:self.number_of_channels] / - records_per_acquisition) - - recordB = np.zeros(self.samples_per_record) - for i in range(self.records_per_buffer): - i0 = (i * self.samples_per_record * self.number_of_channels + 1) - i1 = (i0 + self.samples_per_record * self.number_of_channels) - recordB += (self.buffer[i0:i1:self.number_of_channels] / - records_per_acquisition) - return recordA, recordB - - # DFT AcquisitionController class Demodulation_AcquisitionController(AcquisitionController): """ @@ -159,10 +23,8 @@ def __init__(self, name, alazar_name, demodulation_frequency, **kwargs): self.demodulation_frequency = demodulation_frequency self.acquisitionkwargs = {} self.samples_per_record = None - self.bits_per_sample = None self.records_per_buffer = None self.buffers_per_acquisition = None - self.allocated_buffers = None # TODO(damazter) (S) this is not very general: self.number_of_channels = 2 self.cos_list = None diff --git a/qcodes/instrument_drivers/AlazarTech/HD_test_controller.py b/qcodes/instrument_drivers/AlazarTech/HD_test_controller.py deleted file mode 100644 index f9b4e751cd16..000000000000 --- a/qcodes/instrument_drivers/AlazarTech/HD_test_controller.py +++ /dev/null @@ -1,105 +0,0 @@ -from .ATS import AcquisitionController -import math -import numpy as np -import matplotlib.pyplot as plt - -# HD_ test controller -class Controller(): - def __init__(self, dif_freq): - self.dif_freq = dif_freq - self.sample_rate = 10000000 - self.samples_per_record = 2024 - - record_duration = self.samples_per_record / self.sample_rate - time_period_dif = 1 / self.dif_freq - cycles_measured = record_duration / time_period_dif - oversampling_rate = self.sample_rate / (2 * self.dif_freq) - print("Oscillations per record: {:.2f} (expect 100+)".format(cycles_measured)) - print("Oversampling rate: {:.2f} (expect > 2)".format(oversampling_rate)) - - def pre_start_capture(self): - self.integer_list = np.arange(self.samples_per_record) - self.angle_list = (2 * np.pi * self.dif_freq / self.sample_rate * - self.integer_list) - - self.cos_list = np.cos(self.angle_list) - self.sin_list = np.sin(self.angle_list) - - def fit(self, rec): - # Discrete Fourier Transform - RePart = np.dot(rec, self.cos_list) / self.samples_per_record - ImPart = np.dot(rec, self.sin_list) / self.samples_per_record - # factor of 2 is because amplitude is split between finite term and - # double frequency term which averages to 0 - complex_num = RePart - ImPart*1j - ampl = 2 * abs(complex_num) - phase = np.angle(complex_num, deg=True) - - return [ampl, phase] - -def make_rec(controller, freq, phase_deg): - phase_rad = phase_deg*np.pi/180 - angle_list = (2 * np.pi * freq / controller.sample_rate * - controller.integer_list)+phase_rad - rec = np.cos(angle_list) - return rec - -def main(freq, phase_dif): - # make an acqisition controller and set up sin and cos software signals - c = Controller(freq) - c.pre_start_capture() - - # make a 'hardware' signal with same frequency shifted by phase - # mix with software signals to make real and imaginary parts of mixed signal - rec = make_rec(c, freq, phase_dif) - cos_rec = np.multiply(c.cos_list, rec) - sin_rec = np.multiply(c.sin_list, rec) - - # print averaged magnitude and phase of mixed signal - # should give dc part of signal as double frequency part averages out - print(" mag, phase : "+str(c.fit(rec))) - plt.figure(1) - plt.subplot(311) - plt.plot(c.cos_list[:100], "-r") - plt.plot(c.sin_list[:100], "-g") - plt.title('"software" signals') - plt.subplot(312) - plt.plot(rec[:100]) - plt.title('"hardware" signal') - plt.subplot(313) - plt.plot(cos_rec[:100], "-r") - plt.plot(sin_rec[:100], "-g") - plt.title('re and im parts of mixed signal') - plt.show() - - # sweep 'hardware' signal frequency through software signal and - # observe magnitude and phase response - point_ran = 10000 - sweep_mag = [[],[]] - for i in range(point_ran+1): - freq_ran = 0.2*freq - shift_freq = (freq_ran/point_ran)*(-point_ran/2+i) - rec = make_rec(c, freq+shift_freq, 0) - mag, phase = c.fit(rec) - sweep_mag[1].append(mag) - sweep_mag[0].append(shift_freq) - sweep_phase = [[],[]] - for i in range(point_ran+1): - angle_ran = 45 - shift_phase = (angle_ran/point_ran)*(-point_ran/2+i) - rec = make_rec(c, freq, phase_dif+shift_phase) - mag, phase = c.fit(rec) - sweep_phase[1].append(phase) - sweep_phase[0].append(shift_phase) - plt.figure(1) - plt.subplot(211) - plt.title('sweep:magnitude response') - plt.plot(sweep_mag[0], sweep_mag[1]) - plt.subplot(212) - plt.title('sweep:phase response') - plt.plot(sweep_phase[0], sweep_phase[1]) - plt.show() - - - - # import qcodes.instrument_drivers.AlazarTech.HD_test_controller as cont diff --git a/qcodes/instrument_drivers/AlazarTech/QDev_acquisition_controllers.py b/qcodes/instrument_drivers/AlazarTech/QDev_acquisition_controllers.py deleted file mode 100644 index c34bc08080c6..000000000000 --- a/qcodes/instrument_drivers/AlazarTech/QDev_acquisition_controllers.py +++ /dev/null @@ -1,246 +0,0 @@ -from .ATS import AcquisitionController -from scipy import signal -import numpy as np - - -class Filtering_Controller(): - """Filtering Measurement Controller - If 1 channel: - Mixes reference software signal with input hardware signal, - filters out double frequency component and returns magnitude and - phase data for dc component. Throws away channel 2 - If 2 channels: - Uses two hardware channels to find magnitude and - phase data for dc component - - TODO(nataliejpg) test diffrent filters and window types - TODO(nataliejpg) numtaps logic - TODO(nataliejpg) cutoff logic - TODO(nataliejpg) post aqcuire one input stuff?? - """ - - def __init__(self, dif_freq, numtaps, cutoff, number_of_channels): - self.dif_freq = dif_freq - self.sample_rate = 50000000 - self.samples_per_record = 10024 - self.buffers_per_acquisition = None - self.number_of_channels = number_of_channels - self.numtaps = numtaps - self.cutoff = cutoff - self.delay = numtaps - 1 - self.buffer = None - - def pre_start_capture(self): - record_duration = self.samples_per_record / self.sample_rate - time_period_dif = 1 / self.dif_freq - cycles_measured = record_duration / time_period_dif - oversampling_rate = self.sample_rate / (2 * self.dif_freq) - print("Oscillations per record: {:.2f} (expect 100+)" - .format(cycles_measured)) - print("Oversampling rate: {:.2f} (expect > 2)" - .format(oversampling_rate)) - print("Filtering delay is {} samples".format(self.delay)) - - integer_list = np.arange(self.samples_per_record) - if self.number_of_channels == 2: - angle_list = (2 * np.pi * self.freq_dif / self.sample_rate * - integer_list) - self.cos_list = np.cos(angle_list) - self.sin_list = np.sin(angle_list) - elif self.number_of_channels == 1: - angle_shift = 90 - self.sample_shift = (2 * np.pi * self.freq_dif / self.sample_rate * - angle_shift) - - def pre_acquire(self, alazar): - # gets called after 'AlazarStartCapture' - pass - - def handle_buffer(self, alazar, data): - self.buffer += data - - def post_acquire(self, alazar): - """Average over records in buffer, mix and filter double freq - if 1 chan selected recordA is mixed with software signal - if 2 chan selected recordA and recordB are mixed - """ - records_per_acquisition = (1. * self.buffers_per_acquisition * - self.records_per_buffer) - recordA = np.zeros(self.samples_per_record) - for i in range(self.records_per_buffer): - i0 = (i * self.samples_per_record * self.number_of_channels) - i1 = (i0 + self.samples_per_record * self.number_of_channels) - recordA += (self.buffer[i0:i1:self.number_of_channels] / - records_per_acquisition) - recordB = np.zeros(self.samples_per_record) - for i in range(self.records_per_buffer): - i0 = (i * self.samples_per_record * self.number_of_channels + 1) - i1 = (i0 + self.samples_per_record * self.number_of_channels) - recordB += (self.buffer[i0:i1:self.number_of_channels] / - records_per_acquisition) - - if self.number_of_channels == 1: - mixA_re = np.multiply(self.cos_list, recordA) - mixA_im = np.multiply(self.sin_list, recordA) - mixA_re_filtered = filter(mixA_re) - mixA_im_filtered = filter(mixA_im) - mixA_complex = mixA_re_filtered + mixA_im_filtered * 1j - magA = 2 * abs(mixA_complex) - phaseA = 2 * np.angle(mixA_complex, deg=True) - return magA, phaseA - elif self.number_of_channels == 2: - recordB_shifted = np.roll(recordB, self.sample_shift) - mixAB_re = np.multiply(recordA, recordB) - mixAB_im = np.multiply(recordA, recordB_shifted) - mixAB_re_filtered = filter(mixAB_re) - mixAB_im_filtered = filter(mixAB_im) - mixAB_complex = mixAB_re_filtered + mixAB_im_filtered * 1j - magAB = 2 * abs(mixAB_complex) - phaseAB = 2 * np.angle(mixAB_complex, deg=True) - return magAB, phaseAB - - def filter(self, rec): - """FIR window filter applied to filter out high freq components - """ - sample_rate = self.sample_rate - nyq_rate = sample_rate / 2. - fir_coef = signal.firwin(self.numtaps, self.cutoff / nyq_rate) - filtered_rec = signal.lfilter(fir_coef, 1.0, rec) - return filtered_rec - - -class HD_Controller(AcquisitionController): - """Averaging Heterodyne Measurement Controller - Does averaged DFT on 2 channel Alazar measurement - - TODO(nataliejpg) handling of channel number - TODO(nataliejpg) test angle data - """ - - def __init__(self, freq_dif): - self.freq_dif = freq_dif - self.samples_per_record = None - self.records_per_buffer = None - self.buffers_per_acquisition = None - self.number_of_channels = 2 - self.buffer = None - - def pre_start_capture(self, alazar): - """Get config data from alazar card and set up DFT""" - self.samples_per_record = alazar.samples_per_record() - self.records_per_buffer = alazar.records_per_buffer() - self.buffers_per_acquisition = alazar.buffers_per_acquisition() - self.sample_rate = alazar.get_sample_speed() - self.buffer = np.zeros(self.samples_per_record * - self.records_per_buffer * - self.number_of_channels) - # TODO(nataliejpg) leave explicit or save lines? add error/logging? - averaging = self.buffers_per_acquisition * self.records_per_buffer - record_duration = self.samples_per_record / self.sample_rate - time_period_dif = 1 / self.freq_dif - cycles_measured = record_duration / time_period_dif - oversampling_rate = self.sample_rate / (2 * self.freq_dif) - print("Average over {:.2f} records".format(averaging)) - print("Oscillations per record: {:.2f} (expect 100+)" - .format(cycles_measured)) - print("Oversampling rate: {:.2f} (expect > 2)" - .format(oversampling_rate)) - - integer_list = np.arange(self.samples_per_record) - angle_list = (2 * np.pi * self.freq_dif / self.sample_rate * - integer_list) - self.cos_list = np.cos(angle_list) - self.sin_list = np.sin(angle_list) - - def pre_acquire(self, alazar): - # gets called after 'AlazarStartCapture' - pass - - def handle_buffer(self, alazar, data): - self.buffer += data - - def post_acquire(self, alazar): - """Average over records in buffer and do DFT: - assumes samples are arranged in the buffer as follows: - S0A, S0B, ..., S1A, S1B, ... - with SXY the sample number X of channel Y. - """ - records_per_acquisition = (1. * self.buffers_per_acquisition * - self.records_per_buffer) - recordA = np.zeros(self.samples_per_record) - for i in range(self.records_per_buffer): - i0 = (i * self.samples_per_record * self.number_of_channels) - i1 = (i0 + self.samples_per_record * self.number_of_channels) - recordA += (self.buffer[i0:i1:self.number_of_channels] / - records_per_acquisition) - recordB = np.zeros(self.samples_per_record) - for i in range(self.records_per_buffer): - i0 = (i * self.samples_per_record * self.number_of_channels + 1) - i1 = (i0 + self.samples_per_record * self.number_of_channels) - recordB += (self.buffer[i0:i1:self.number_of_channels] / - records_per_acquisition) - - resA = self.fit(recordA) - resB = self.fit(recordB) - - return resA, resB - - def fit(self, rec): - """Do Discrete Fourier Transform and return magnitude and phase data""" - RePart = np.dot(rec, self.cos_list) / self.samples_per_record - ImPart = np.dot(rec, self.sin_list) / self.samples_per_record - complex_num = RePart - ImPart * 1j - # factor of 2 in ampl is due to loss of averaged double frequency term - ampl = 2 * abs(complex_num) - phase = np.angle(complex_num, deg=True) - return [ampl, phase] - - -class Basic_AcquisitionController(AcquisitionController): - """Basic AcquisitionController tested on ATS9360 - returns unprocessed data averaged by record with 2 channels - """ - - def __init__(self): - self.samples_per_record = None - self.records_per_buffer = None - self.buffers_per_acquisition = None - self.number_of_channels = 2 - self.buffer = None - - def pre_start_capture(self, alazar): - self.samples_per_record = alazar.samples_per_record() - self.records_per_buffer = alazar.records_per_buffer() - self.buffers_per_acquisition = alazar.buffers_per_acquisition() - self.buffer = np.zeros(self.samples_per_record * - self.records_per_buffer * - self.number_of_channels) - - def pre_acquire(self, alazar): - # gets called after 'AlazarStartCapture' - pass - - def handle_buffer(self, alazar, data): - self.buffer += data - - def post_acquire(self, alazar): - # average over records in buffer: - # for ATS9360 samples are arranged in the buffer as follows: - # S0A, S0B, ..., S1A, S1B, ... - # with SXY the sample number X of channel Y. - records_per_acquisition = (self.buffers_per_acquisition * - self.records_per_buffer) - recordA = np.zeros(self.samples_per_record) - for i in range(self.records_per_buffer): - i0 = (i * self.samples_per_record * self.number_of_channels) - i1 = (i0 + self.samples_per_record * self.number_of_channels) - recordA += (self.buffer[i0:i1:self.number_of_channels] / - records_per_acquisition) - - recordB = np.zeros(self.samples_per_record) - for i in range(self.records_per_buffer): - i0 = (i * self.samples_per_record * self.number_of_channels + 1) - i1 = (i0 + self.samples_per_record * self.number_of_channels) - recordB += (self.buffer[i0:i1:self.number_of_channels] / - records_per_acquisition) - return recordA, recordB diff --git a/qcodes/instrument_drivers/AlazarTech/filter1_test_controller.py b/qcodes/instrument_drivers/AlazarTech/filter1_test_controller.py deleted file mode 100644 index 6efae1d80321..000000000000 --- a/qcodes/instrument_drivers/AlazarTech/filter1_test_controller.py +++ /dev/null @@ -1,124 +0,0 @@ -import numpy as np -import matplotlib.pyplot as plt -from scipy import signal - -# one channel filter controller -class Controller(): - def __init__(self, dif_freq): - self.dif_freq = dif_freq - self.sample_rate = 500000000 - self.samples_per_record = 10024 - - record_duration = self.samples_per_record/self.sample_rate - dif_time_period = 1/self.dif_freq - cycles_measured = record_duration / dif_time_period - oversampling_rate = self.sample_rate/(2*self.dif_freq) - print("Measuring from "+str(cycles_measured)+" samples should be on the order of 100 at least") - print("Oversampling rate is "+str(oversampling_rate)+" should be > 2") - - def pre_start_capture(self): - self.integer_list = np.arange(self.samples_per_record) - self.angle_list = (2 * np.pi * self.dif_freq / self.sample_rate * - self.integer_list) - - self.cos_list = np.cos(self.angle_list) - self.sin_list = np.sin(self.angle_list) - -def filter(controller, rec, numtaps, cutoff): - sample_rate = controller.sample_rate - print("sample_rate is "+str(sample_rate)) - nyq_rate = sample_rate / 2. - print("nyq_rate is "+ str(nyq_rate)) - delay = (numtaps-1) - fir_coef = signal.firwin(numtaps, cutoff/nyq_rate, window = "hamming") - filtered_rec = 2 * signal.lfilter(fir_coef, 1.0, rec) - w, h = signal.freqz(fir_coef) - plt.plot(w*controller.sample_rate/(2*np.pi), 20 * np.log10(abs(h)), 'b') - plt.ylabel('Amplitude [dB]', color='b') - plt.xlabel('Frequency [rad/sample]') - plt.show() - return fir_coef, filtered_rec, delay - -def make_rec(controller, freq, ampdif, phase_deg): - phase_rad = phase_deg*2*np.pi/360 - angle_list = (2 * np.pi * freq * controller.integer_list / controller.sample_rate)+phase_rad - rec = ampdif*np.cos(angle_list) - return rec - -def main(freq1, freq2, phasedif, ampdif, numtaps, cutoff): - # make an acquisition controller (and sampling rate warnings related to - # given freq1) and integer list used for signal making - c = Controller(freq1) - c.pre_start_capture() - - # make one hardware signal with given frequency and phase difference - # make a software signal with real and imaginary parts - # plot first 100 points of each - rec1 = make_rec(c, freq2, phase_deg=phasedif, ampdif=ampdif) - plt.figure(1) - plt.subplot(211) - plt.plot(rec1[:100]) - plt.subplot(212) - plt.plot(rec2[:100]) - plt.show() - - # mix hardware signals and filter out frequencies above cutoff - # should leave only freq_dif component - rec12 = np.multiply(rec1, rec2) - fir_coef, filtered_rec_12 = filter(c, rec12, numtaps, cutoff) - delay = numtaps-1 - - # plot filter - w, h = signal.freqz(fir_coef) - freq_axis = (w/np.pi)*c.sample_rate - fig = plt.figure(1) - ax1 = fig.add_subplot(111) - plt.title('Digital filter frequency response') - plt.plot(freq_axis, 20 * np.log10(abs(h)), 'b') - plt.ylabel('Amplitude [dB]', color='b') - plt.xlabel('Frequency [Hz]') - - ax2 = ax1.twinx() - angles = np.unwrap(np.angle(h)) - plt.plot(freq_axis, angles, 'g') - plt.ylabel('Angle (radians)', color='g') - plt.grid() - plt.axis('tight') - plt.show() - - - # shift first hardware signal by 90 degrees to make - # sin signal for mixing, mix and plot - rec3 = make_rec(c, freq1, sin=True) - rec23 = np.multiply(rec2, rec3) - fir_coef, filtered_rec_23 = filter(c, rec23, numtaps, cutoff) - - # plot filtered signal and magnitude and phase results - plt.figure(1) - plt.subplot(411) - plt.title('Re part') - plt.plot(rec12, '-r') - plt.ylabel('unfiltered record', color='r') - plt.plot(filtered_rec_12, '-g') - plt.ylabel('filtered record', color='g') - plt.subplot(412) - plt.title('Im part') - plt.plot(rec23, '-r') - plt.ylabel('unfiltered record', color='r') - plt.plot(filtered_rec_23, '-g') - plt.ylabel('filtered record', color='g') - - # add to plot magnitude and phase of resulting complex number - complex_rec = filtered_rec_12+filtered_rec_23*1j - magnitude = abs(complex_rec) - phase = np.angle(complex_rec, deg=True) - plt.subplot(413) - plt.title('Magnitude') - plt.plot(magnitude) - plt.ylabel('magnitude') - plt.subplot(414) - plt.title('Phase') - plt.plot(phase) - plt.ylabel('phase') - plt.show() - diff --git a/qcodes/instrument_drivers/AlazarTech/filter2_test_controller.py b/qcodes/instrument_drivers/AlazarTech/filter2_test_controller.py deleted file mode 100644 index 7d0838f16579..000000000000 --- a/qcodes/instrument_drivers/AlazarTech/filter2_test_controller.py +++ /dev/null @@ -1,126 +0,0 @@ -import numpy as np -import matplotlib.pyplot as plt -from scipy import signal - -# two channel filter controller -class Controller(): - def __init__(self, dif_freq): - self.dif_freq = dif_freq - self.sample_rate = 50000000 - self.samples_per_record = 10024 - - record_duration = self.samples_per_record / self.sample_rate - time_period_dif = 1 / self.dif_freq - cycles_measured = record_duration / time_period_dif - oversampling_rate = self.sample_rate / (2 * self.dif_freq) - print("Oscillations per record: {:.2f} (expect 100+)".format(cycles_measured)) - print("Oversampling rate: {:.2f} (expect > 2)".format(oversampling_rate)) - - def pre_start_capture(self): - self.integer_list = np.arange(self.samples_per_record) - -def filter(controller, rec, numtaps, cutoff): - sample_rate = controller.sample_rate - nyq_rate = sample_rate / 2. - fir_coef = signal.firwin(numtaps, cutoff/nyq_rate) - filtered_rec = 2 * signal.lfilter(fir_coef, 1.0, rec) - return fir_coef, filtered_rec - -def filterls(controller, rec, numtaps, cutoff): - sample_rate = controller.sample_rate - nyq_rate = sample_rate / 2. - bands = [0, cutoff/nyq_rate, cutoff/nyq_rate, 1] - desired = [1, 1, 0, 0] - fir_coef = signal.firls(numtaps, bands, desired, nyq=nyq_rate) - filtered_rec = 2 * signal.lfilter(fir_coef, 1.0, rec) - return fir_coef, filtered_rec - -def make_rec(controller, freq, phase_deg=0, ampdif=1, sin=False): - phase_rad = phase_deg*2*np.pi/360 - angle_list = (2 * np.pi * freq * controller.integer_list / controller.sample_rate)+phase_rad - rec = ampdif*np.cos(angle_list) - if sin: - rec = ampdif*np.sin(angle_list) - return rec - - -def main(freq1, freq2, phasedif, ampdif, numtaps, cutoff): - # make an acquisition controller (and sampling rate warnings related to - # given freq1) and integer list used for signal making - c = Controller(freq1) - c.pre_start_capture() - - # make two hardware signals with given frequencies and phase difference - # plot first 100 points of each - rec1 = make_rec(c, freq1) - rec2 = make_rec(c, freq2, phase_deg=phasedif, ampdif=ampdif) - plt.figure(1) - plt.subplot(211) - plt.plot(rec1[:100]) - plt.subplot(212) - plt.plot(rec2[:100]) - plt.show() - - # mix hardware signals and filter out frequencies above cutoff - # should leave only freq_dif component - rec12 = np.multiply(rec1, rec2) - fir_coef, filtered_rec_12 = filterls(c, rec12, numtaps, cutoff) - delay = numtaps-1 - - # plot filter - w, h = signal.freqz(fir_coef) - freq_axis = (w/np.pi)*c.sample_rate - fig = plt.figure(1) - ax1 = fig.add_subplot(111) - plt.title('Digital filter frequency response') - plt.plot(freq_axis, 20 * np.log10(abs(h)), 'b') - plt.ylabel('Amplitude [dB]', color='b') - plt.xlabel('Frequency [Hz]') - - ax2 = ax1.twinx() - angles = np.unwrap(np.angle(h)) - plt.plot(freq_axis, angles, 'g') - plt.ylabel('Angle (radians)', color='g') - plt.grid() - plt.axis('tight') - plt.show() - - - # shift first hardware signal by 90 degrees to make - # sin signal for mixing, mix and plot - rec3 = make_rec(c, freq1, sin=True) - rec23 = np.multiply(rec2, rec3) - fir_coef, filtered_rec_23 = filterls(c, rec23, numtaps, cutoff) - - # plot filtered signal and magnitude and phase results - plt.figure(1) - plt.subplot(411) - plt.title('Re part') - plt.plot(rec12, '-r') - plt.ylabel('unfiltered record', color='r') - plt.plot(filtered_rec_12, '-g') - plt.ylabel('filtered record', color='g') - plt.subplot(412) - plt.title('Im part') - plt.plot(rec23, '-r') - plt.ylabel('unfiltered record', color='r') - plt.plot(filtered_rec_23, '-g') - plt.ylabel('filtered record', color='g') - - # add to plot magnitude and phase of resulting complex number - complex_rec = filtered_rec_12+filtered_rec_23*1j - magnitude = abs(complex_rec) - phase = np.angle(complex_rec, deg=True) - plt.subplot(413) - plt.title('Magnitude') - plt.plot(magnitude) - plt.ylabel('magnitude') - plt.subplot(414) - plt.title('Phase') - plt.plot(phase) - plt.ylabel('phase') - plt.show() - - - - # import qcodes.instrument_drivers.AlazarTech.filter2_test_controller as cont From 51d95addfda68dcd4f81512ec9ec4b686973ebcd Mon Sep 17 00:00:00 2001 From: Natalie Pearson Date: Sat, 19 Nov 2016 20:29:19 +0100 Subject: [PATCH 14/17] replace accidentally deleted alazar example notebook --- docs/examples/Qcodes example ATS_ONWORK.ipynb | 1658 +++++++++++++++++ 1 file changed, 1658 insertions(+) create mode 100644 docs/examples/Qcodes example ATS_ONWORK.ipynb diff --git a/docs/examples/Qcodes example ATS_ONWORK.ipynb b/docs/examples/Qcodes example ATS_ONWORK.ipynb new file mode 100644 index 000000000000..eac33345b0ae --- /dev/null +++ b/docs/examples/Qcodes example ATS_ONWORK.ipynb @@ -0,0 +1,1658 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "application/javascript": [ + "/*\r\n", + " * Qcodes Jupyter/IPython widgets\r\n", + " */\r\n", + "require([\r\n", + " 'nbextensions/widgets/widgets/js/widget',\r\n", + " 'nbextensions/widgets/widgets/js/manager'\r\n", + "], function (widget, manager) {\r\n", + "\r\n", + " var UpdateView = widget.DOMWidgetView.extend({\r\n", + " render: function() {\r\n", + " window.MYWIDGET = this;\r\n", + " this._interval = 0;\r\n", + " this.update();\r\n", + " },\r\n", + " update: function() {\r\n", + " this.display(this.model.get('_message'));\r\n", + " this.setInterval();\r\n", + " },\r\n", + " display: function(message) {\r\n", + " /*\r\n", + " * display method: override this for custom display logic\r\n", + " */\r\n", + " this.el.innerHTML = message;\r\n", + " },\r\n", + " remove: function() {\r\n", + " clearInterval(this._updater);\r\n", + " },\r\n", + " setInterval: function(newInterval) {\r\n", + " var me = this;\r\n", + " if(newInterval===undefined) newInterval = me.model.get('interval');\r\n", + " if(newInterval===me._interval) return;\r\n", + "\r\n", + " me._interval = newInterval;\r\n", + "\r\n", + " if(me._updater) clearInterval(me._updater);\r\n", + "\r\n", + " if(me._interval) {\r\n", + " me._updater = setInterval(function() {\r\n", + " me.send({myupdate: true});\r\n", + " if(!me.model.comm_live) {\r\n", + " console.log('missing comm, canceling widget updates', me);\r\n", + " clearInterval(me._updater);\r\n", + " }\r\n", + " }, me._interval * 1000);\r\n", + " }\r\n", + " }\r\n", + " });\r\n", + " manager.WidgetManager.register_widget_view('UpdateView', UpdateView);\r\n", + "\r\n", + " var HiddenUpdateView = UpdateView.extend({\r\n", + " display: function(message) {\r\n", + " this.$el.hide();\r\n", + " }\r\n", + " });\r\n", + " manager.WidgetManager.register_widget_view('HiddenUpdateView', HiddenUpdateView);\r\n", + "\r\n", + " var SubprocessView = UpdateView.extend({\r\n", + " render: function() {\r\n", + " var me = this;\r\n", + " me._interval = 0;\r\n", + " me._minimize = '';\r\n", + " me._restore = '';\r\n", + "\r\n", + " // max lines of output to show\r\n", + " me.maxOutputLength = 500;\r\n", + "\r\n", + " // in case there is already an outputView present,\r\n", + " // like from before restarting the kernel\r\n", + " $('.qcodes-output-view').not(me.$el).remove();\r\n", + "\r\n", + " me.$el\r\n", + " .addClass('qcodes-output-view')\r\n", + " .attr('qcodes-state', 'docked')\r\n", + " .html(\r\n", + " '
' +\r\n", + " '
' +\r\n", + " '' +\r\n", + " '' +\r\n", + " '' +\r\n", + " '' +\r\n", + " '' +\r\n", + " '' +\r\n", + " '
' +\r\n", + " '
'\r\n",
+       "                );\r\n",
+       "\r\n",
+       "            me.clearButton = me.$el.find('.qcodes-clear-output');\r\n",
+       "            me.minButton = me.$el.find('.qcodes-minimize');\r\n",
+       "            me.outputArea = me.$el.find('pre');\r\n",
+       "            me.subprocessList = me.$el.find('.qcodes-process-list');\r\n",
+       "            me.abortButton = me.$el.find('.qcodes-abort-loop');\r\n",
+       "            me.processLinesButton = me.$el.find('.qcodes-processlines')\r\n",
+       "\r\n",
+       "            me.outputLines = [];\r\n",
+       "\r\n",
+       "            me.clearButton.click(function() {\r\n",
+       "                me.outputArea.html('');\r\n",
+       "                me.clearButton.addClass('disabled');\r\n",
+       "            });\r\n",
+       "\r\n",
+       "            me.abortButton.click(function() {\r\n",
+       "                me.send({abort: true});\r\n",
+       "            });\r\n",
+       "\r\n",
+       "            me.processLinesButton.click(function() {\r\n",
+       "                // toggle multiline process list display\r\n",
+       "                me.subprocessesMultiline = !me.subprocessesMultiline;\r\n",
+       "                me.showSubprocesses();\r\n",
+       "            });\r\n",
+       "\r\n",
+       "            me.$el.find('.js-state').click(function() {\r\n",
+       "                var state = this.className.substr(this.className.indexOf('qcodes'))\r\n",
+       "                        .split('-')[1].split(' ')[0];\r\n",
+       "                me.model.set('_state', state);\r\n",
+       "            });\r\n",
+       "\r\n",
+       "            $(window)\r\n",
+       "                .off('resize.qcodes')\r\n",
+       "                .on('resize.qcodes', function() {me.clipBounds();});\r\n",
+       "\r\n",
+       "            me.update();\r\n",
+       "        },\r\n",
+       "\r\n",
+       "        updateState: function() {\r\n",
+       "            var me = this,\r\n",
+       "                oldState = me.$el.attr('qcodes-state'),\r\n",
+       "                state = me.model.get('_state');\r\n",
+       "\r\n",
+       "            if(state === oldState) return;\r\n",
+       "\r\n",
+       "            setTimeout(function() {\r\n",
+       "                // not sure why I can't pop it out of the widgetarea in render, but it seems that\r\n",
+       "                // some other bit of code resets the parent after render if I do it there.\r\n",
+       "                // To be safe, just do it on every state click.\r\n",
+       "                me.$el.appendTo('body');\r\n",
+       "\r\n",
+       "                if(oldState === 'floated') {\r\n",
+       "                    console.log('here');\r\n",
+       "                    me.$el.draggable('destroy').css({left:'', top: ''});\r\n",
+       "                }\r\n",
+       "\r\n",
+       "                me.$el.attr('qcodes-state', state);\r\n",
+       "\r\n",
+       "                if(state === 'floated') {\r\n",
+       "                    me.$el\r\n",
+       "                        .draggable({stop: function() { me.clipBounds(); }})\r\n",
+       "                        .css({\r\n",
+       "                            left: window.innerWidth - me.$el.width() - 15,\r\n",
+       "                            top: window.innerHeight - me.$el.height() - 10\r\n",
+       "                        });\r\n",
+       "                }\r\n",
+       "\r\n",
+       "                // any previous highlighting is now moot\r\n",
+       "                me.$el.removeClass('qcodes-highlight');\r\n",
+       "            }, 0);\r\n",
+       "\r\n",
+       "        },\r\n",
+       "\r\n",
+       "        clipBounds: function() {\r\n",
+       "            var me = this;\r\n",
+       "            if(me.$el.attr('qcodes-state') === 'floated') {\r\n",
+       "                var bounds = me.$el[0].getBoundingClientRect(),\r\n",
+       "                    minVis = 40,\r\n",
+       "                    maxLeft = window.innerWidth - minVis,\r\n",
+       "                    minLeft = minVis - bounds.width,\r\n",
+       "                    maxTop = window.innerHeight - minVis;\r\n",
+       "\r\n",
+       "                if(bounds.left > maxLeft) me.$el.css('left', maxLeft);\r\n",
+       "                else if(bounds.left < minLeft) me.$el.css('left', minLeft);\r\n",
+       "\r\n",
+       "                if(bounds.top > maxTop) me.$el.css('top', maxTop);\r\n",
+       "                else if(bounds.top < 0) me.$el.css('top', 0);\r\n",
+       "            }\r\n",
+       "        },\r\n",
+       "\r\n",
+       "        display: function(message) {\r\n",
+       "            var me = this;\r\n",
+       "            if(message) {\r\n",
+       "                var initialScroll = me.outputArea.scrollTop();\r\n",
+       "                me.outputArea.scrollTop(me.outputArea.prop('scrollHeight'));\r\n",
+       "                var scrollBottom = me.outputArea.scrollTop();\r\n",
+       "\r\n",
+       "                if(me.$el.attr('qcodes-state') === 'minimized') {\r\n",
+       "                    // if we add text and the box is minimized, highlight the\r\n",
+       "                    // title bar to alert the user that there are new messages.\r\n",
+       "                    // remove then add the class, so we get the animation again\r\n",
+       "                    // if it's already highlighted\r\n",
+       "                    me.$el.removeClass('qcodes-highlight');\r\n",
+       "                    setTimeout(function(){\r\n",
+       "                        me.$el.addClass('qcodes-highlight');\r\n",
+       "                    }, 0);\r\n",
+       "                }\r\n",
+       "\r\n",
+       "                var newLines = message.split('\\n'),\r\n",
+       "                    out = me.outputLines,\r\n",
+       "                    outLen = out.length;\r\n",
+       "                if(outLen) out[outLen - 1] += newLines[0];\r\n",
+       "                else out.push(newLines[0]);\r\n",
+       "\r\n",
+       "                for(var i = 1; i < newLines.length; i++) {\r\n",
+       "                    out.push(newLines[i]);\r\n",
+       "                }\r\n",
+       "\r\n",
+       "                if(out.length > me.maxOutputLength) {\r\n",
+       "                    out.splice(0, out.length - me.maxOutputLength + 1,\r\n",
+       "                        '<<< Output clipped >>>');\r\n",
+       "                }\r\n",
+       "\r\n",
+       "                me.outputArea.text(out.join('\\n'));\r\n",
+       "                me.clearButton.removeClass('disabled');\r\n",
+       "\r\n",
+       "                // if we were scrolled to the bottom initially, make sure\r\n",
+       "                // we stay that way.\r\n",
+       "                me.outputArea.scrollTop(initialScroll === scrollBottom ?\r\n",
+       "                    me.outputArea.prop('scrollHeight') : initialScroll);\r\n",
+       "            }\r\n",
+       "\r\n",
+       "            me.showSubprocesses();\r\n",
+       "            me.updateState();\r\n",
+       "        },\r\n",
+       "\r\n",
+       "        showSubprocesses: function() {\r\n",
+       "            var me = this,\r\n",
+       "                replacer = me.subprocessesMultiline ? '
' : ', ',\r\n", + " processes = (me.model.get('_processes') || '')\r\n", + " .replace(/\\n/g, '>' + replacer + '<');\r\n", + "\r\n", + " if(processes) processes = '<' + processes + '>';\r\n", + " else processes = 'No subprocesses';\r\n", + "\r\n", + " me.abortButton.toggleClass('disabled', processes.indexOf('Measurement')===-1);\r\n", + "\r\n", + " me.subprocessList.html(processes);\r\n", + " }\r\n", + " });\r\n", + " manager.WidgetManager.register_widget_view('SubprocessView', SubprocessView);\r\n", + "});\r\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "No loop running\n" + ] + } + ], + "source": [ + "# import all necessary things\n", + "%matplotlib nbagg\n", + "\n", + "import qcodes as qc\n", + "import qcodes.instrument.parameter as parameter\n", + "import qcodes.instrument_drivers.AlazarTech.ATS9870 as ATSdriver\n", + "import qcodes.instrument_drivers.AlazarTech.ATS_acquisition_controllers as ats_contr\n", + "\n", + "qc.halt_bg()\n", + "qc.set_mp_method('spawn') # force Windows behavior on mac\n", + "\n", + "# this makes a widget in the corner of the window to show and control\n", + "# subprocesses and any output they would print to the terminal\n", + "qc.show_subprocess_widget()" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[{'bits_per_sample': 8,\n", + " 'board_id': 1,\n", + " 'board_kind': 'ATS9870',\n", + " 'max_samples': 4294966272,\n", + " 'system_id': 1}]" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Command to list all alazar boards connected to the system\n", + "ATSdriver.AlazarTech_ATS.find_boards()" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'CPLD_version': '13.8',\n", + " 'SDK_version': '5.10.15',\n", + " 'asopc_type': '2435577968',\n", + " 'driver_version': '5.10.15',\n", + " 'firmware': None,\n", + " 'latest_cal_date': '30-01-13',\n", + " 'memory_size': '4294966272',\n", + " 'model': 'ATS9870',\n", + " 'pcie_link_speed': '0.25GB/s',\n", + " 'pcie_link_width': '8',\n", + " 'serial': '910266',\n", + " 'vendor': 'AlazarTech'}" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Create the ATS9870 instrument on the new server \"alazar_server\"\n", + "ats_inst = ATSdriver.AlazarTech_ATS9870(name='Alazar1', server_name=\"alazar_server\")\n", + "# Print all information about this Alazar card\n", + "ats_inst.get_idn()" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Instantiate an acquisition controller (In this case we are doing a simple DFT) on the same server (\"alazar_server\") and \n", + "# provide the name of the name of the alazar card that this controller should control\n", + "acquisition_controller = ats_contr.Demodulation_AcquisitionController(name='acquisition_controller', \n", + " demodulation_frequency=10e6, \n", + " alazar_name='Alazar1', \n", + " server_name=\"alazar_server\")" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# Configure all settings in the Alazar card\n", + "ats_inst.config(#clock_source='INTERNAL_CLOCK',\n", + " sample_rate=100000000,\n", + " #clock_edge='CLOCK_EDGE_RISING',\n", + " #decimation=0,\n", + " #coupling=['AC','AC'],\n", + " channel_range=[2.,2.],\n", + " #impedance=[50,50],\n", + " #bwlimit=['DISABLED','DISABLED'],\n", + " #trigger_operation='TRIG_ENGINE_OP_J',\n", + " #trigger_engine1='TRIG_ENGINE_J',\n", + " trigger_source1='EXTERNAL',\n", + " #trigger_slope1='TRIG_SLOPE_POSITIVE',\n", + " #trigger_level1=128,\n", + " #trigger_engine2='TRIG_ENGINE_K',\n", + " #trigger_source2='DISABLE',\n", + " #trigger_slope2='TRIG_SLOPE_POSITIVE',\n", + " #trigger_level2=128,\n", + " #external_trigger_coupling='AC',\n", + " #external_trigger_range='ETR_5V',\n", + " #trigger_delay=0,\n", + " #timeout_ticks=0\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# This command is specific to this acquisition controller. The kwargs provided here are being forwarded to ats_inst.acquire\n", + "# This way, it becomes easy to change acquisition specific settings from the ipython notebook\n", + "acquisition_controller.update_acquisitionkwargs(#mode='NPT',\n", + " samples_per_record=1024,\n", + " records_per_buffer=70,\n", + " buffers_per_acquisition=1,\n", + " #channel_selection='AB',\n", + " #transfer_offset=0,\n", + " #external_startcapture='ENABLED',\n", + " #enable_record_headers='DISABLED',\n", + " #alloc_buffers='DISABLED',\n", + " #fifo_only_streaming='DISABLED',\n", + " #interleave_samples='DISABLED',\n", + " #get_processed_data='DISABLED',\n", + " allocated_buffers=1,\n", + " #buffer_timeout=1000\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "0.1205605630942215" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Getting the value of the parameter 'acquisition' of the instrument 'acquisition_controller' performes the entire acquisition \n", + "# protocol. This again depends on the specific implementation of the acquisition controller\n", + "acquisition_controller.acquisition()" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'__class__': 'qcodes.instrument_drivers.AlazarTech.ATS9870.AlazarTech_ATS9870',\n", + " 'functions': {},\n", + " 'name': 'Alazar1',\n", + " 'parameters': {'IDN': {'__class__': 'qcodes.instrument.parameter.StandardParameter',\n", + " 'instrument': 'qcodes.instrument_drivers.AlazarTech.ATS9870.AlazarTech_ATS9870',\n", + " 'instrument_name': 'Alazar1',\n", + " 'label': 'IDN',\n", + " 'name': 'IDN',\n", + " 'ts': '2016-09-21 18:04:30',\n", + " 'units': '',\n", + " 'value': {'CPLD_version': '13.8',\n", + " 'SDK_version': '5.10.15',\n", + " 'asopc_type': '2435577968',\n", + " 'driver_version': '5.10.15',\n", + " 'firmware': None,\n", + " 'latest_cal_date': '30-01-13',\n", + " 'memory_size': '4294966272',\n", + " 'model': 'ATS9870',\n", + " 'pcie_link_speed': '0.25GB/s',\n", + " 'pcie_link_width': '8',\n", + " 'serial': '910266',\n", + " 'vendor': 'AlazarTech'}},\n", + " 'alloc_buffers': {'__class__': 'qcodes.instrument_drivers.AlazarTech.ATS.AlazarParameter',\n", + " 'label': 'Alloc Buffers',\n", + " 'name': 'alloc_buffers',\n", + " 'ts': '2016-09-21 18:04:30',\n", + " 'units': '',\n", + " 'value': 'DISABLED'},\n", + " 'allocated_buffers': {'__class__': 'qcodes.instrument_drivers.AlazarTech.ATS.AlazarParameter',\n", + " 'label': 'Allocated Buffers',\n", + " 'name': 'allocated_buffers',\n", + " 'ts': '2016-09-21 18:04:30',\n", + " 'units': '',\n", + " 'value': 1},\n", + " 'buffer_timeout': {'__class__': 'qcodes.instrument_drivers.AlazarTech.ATS.AlazarParameter',\n", + " 'label': 'Buffer Timeout',\n", + " 'name': 'buffer_timeout',\n", + " 'ts': '2016-09-21 18:04:30',\n", + " 'units': 'ms',\n", + " 'value': 1000},\n", + " 'buffers_per_acquisition': {'__class__': 'qcodes.instrument_drivers.AlazarTech.ATS.AlazarParameter',\n", + " 'label': 'Buffers per Acquisition',\n", + " 'name': 'buffers_per_acquisition',\n", + " 'ts': '2016-09-21 18:04:30',\n", + " 'units': '',\n", + " 'value': 1},\n", + " 'bwlimit1': {'__class__': 'qcodes.instrument_drivers.AlazarTech.ATS.AlazarParameter',\n", + " 'label': 'Bandwidth limit channel 1',\n", + " 'name': 'bwlimit1',\n", + " 'ts': '2016-09-21 18:04:30',\n", + " 'units': '',\n", + " 'value': 'DISABLED'},\n", + " 'bwlimit2': {'__class__': 'qcodes.instrument_drivers.AlazarTech.ATS.AlazarParameter',\n", + " 'label': 'Bandwidth limit channel 2',\n", + " 'name': 'bwlimit2',\n", + " 'ts': '2016-09-21 18:04:30',\n", + " 'units': '',\n", + " 'value': 'DISABLED'},\n", + " 'channel_range1': {'__class__': 'qcodes.instrument_drivers.AlazarTech.ATS.AlazarParameter',\n", + " 'label': 'Range channel 1',\n", + " 'name': 'channel_range1',\n", + " 'ts': '2016-09-21 18:04:30',\n", + " 'units': 'V',\n", + " 'value': 2.0},\n", + " 'channel_range2': {'__class__': 'qcodes.instrument_drivers.AlazarTech.ATS.AlazarParameter',\n", + " 'label': 'Range channel 2',\n", + " 'name': 'channel_range2',\n", + " 'ts': '2016-09-21 18:04:30',\n", + " 'units': 'V',\n", + " 'value': 2.0},\n", + " 'channel_selection': {'__class__': 'qcodes.instrument_drivers.AlazarTech.ATS.AlazarParameter',\n", + " 'label': 'Channel Selection',\n", + " 'name': 'channel_selection',\n", + " 'ts': '2016-09-21 18:04:30',\n", + " 'units': '',\n", + " 'value': 'AB'},\n", + " 'clock_edge': {'__class__': 'qcodes.instrument_drivers.AlazarTech.ATS.AlazarParameter',\n", + " 'label': 'Clock Edge',\n", + " 'name': 'clock_edge',\n", + " 'ts': '2016-09-21 18:04:30',\n", + " 'units': '',\n", + " 'value': 'CLOCK_EDGE_RISING'},\n", + " 'clock_source': {'__class__': 'qcodes.instrument_drivers.AlazarTech.ATS.AlazarParameter',\n", + " 'label': 'Clock Source',\n", + " 'name': 'clock_source',\n", + " 'ts': '2016-09-21 18:04:30',\n", + " 'units': '',\n", + " 'value': 'INTERNAL_CLOCK'},\n", + " 'coupling1': {'__class__': 'qcodes.instrument_drivers.AlazarTech.ATS.AlazarParameter',\n", + " 'label': 'Coupling channel 1',\n", + " 'name': 'coupling1',\n", + " 'ts': '2016-09-21 18:04:30',\n", + " 'units': '',\n", + " 'value': 'AC'},\n", + " 'coupling2': {'__class__': 'qcodes.instrument_drivers.AlazarTech.ATS.AlazarParameter',\n", + " 'label': 'Coupling channel 2',\n", + " 'name': 'coupling2',\n", + " 'ts': '2016-09-21 18:04:30',\n", + " 'units': '',\n", + " 'value': 'AC'},\n", + " 'decimation': {'__class__': 'qcodes.instrument_drivers.AlazarTech.ATS.AlazarParameter',\n", + " 'label': 'Decimation',\n", + " 'name': 'decimation',\n", + " 'ts': '2016-09-21 18:04:30',\n", + " 'units': '',\n", + " 'value': 0},\n", + " 'enable_record_headers': {'__class__': 'qcodes.instrument_drivers.AlazarTech.ATS.AlazarParameter',\n", + " 'label': 'Enable Record Headers',\n", + " 'name': 'enable_record_headers',\n", + " 'ts': '2016-09-21 18:04:30',\n", + " 'units': '',\n", + " 'value': 'DISABLED'},\n", + " 'external_startcapture': {'__class__': 'qcodes.instrument_drivers.AlazarTech.ATS.AlazarParameter',\n", + " 'label': 'External Startcapture',\n", + " 'name': 'external_startcapture',\n", + " 'ts': '2016-09-21 18:04:30',\n", + " 'units': '',\n", + " 'value': 'ENABLED'},\n", + " 'external_trigger_coupling': {'__class__': 'qcodes.instrument_drivers.AlazarTech.ATS.AlazarParameter',\n", + " 'label': 'External Trigger Coupling',\n", + " 'name': 'external_trigger_coupling',\n", + " 'ts': '2016-09-21 18:04:30',\n", + " 'units': '',\n", + " 'value': 'AC'},\n", + " 'external_trigger_range': {'__class__': 'qcodes.instrument_drivers.AlazarTech.ATS.AlazarParameter',\n", + " 'label': 'External Trigger Range',\n", + " 'name': 'external_trigger_range',\n", + " 'ts': '2016-09-21 18:04:30',\n", + " 'units': '',\n", + " 'value': 'ETR_5V'},\n", + " 'fifo_only_streaming': {'__class__': 'qcodes.instrument_drivers.AlazarTech.ATS.AlazarParameter',\n", + " 'label': 'Fifo Only Streaming',\n", + " 'name': 'fifo_only_streaming',\n", + " 'ts': '2016-09-21 18:04:30',\n", + " 'units': '',\n", + " 'value': 'DISABLED'},\n", + " 'get_processed_data': {'__class__': 'qcodes.instrument_drivers.AlazarTech.ATS.AlazarParameter',\n", + " 'label': 'Get Processed Data',\n", + " 'name': 'get_processed_data',\n", + " 'ts': '2016-09-21 18:04:30',\n", + " 'units': '',\n", + " 'value': 'DISABLED'},\n", + " 'impedance1': {'__class__': 'qcodes.instrument_drivers.AlazarTech.ATS.AlazarParameter',\n", + " 'label': 'Impedance channel 1',\n", + " 'name': 'impedance1',\n", + " 'ts': '2016-09-21 18:04:30',\n", + " 'units': 'Ohm',\n", + " 'value': 50},\n", + " 'impedance2': {'__class__': 'qcodes.instrument_drivers.AlazarTech.ATS.AlazarParameter',\n", + " 'label': 'Impedance channel 2',\n", + " 'name': 'impedance2',\n", + " 'ts': '2016-09-21 18:04:30',\n", + " 'units': 'Ohm',\n", + " 'value': 50},\n", + " 'interleave_samples': {'__class__': 'qcodes.instrument_drivers.AlazarTech.ATS.AlazarParameter',\n", + " 'label': 'Interleave Samples',\n", + " 'name': 'interleave_samples',\n", + " 'ts': '2016-09-21 18:04:30',\n", + " 'units': '',\n", + " 'value': 'DISABLED'},\n", + " 'mode': {'__class__': 'qcodes.instrument_drivers.AlazarTech.ATS.AlazarParameter',\n", + " 'label': 'Acquisiton mode',\n", + " 'name': 'mode',\n", + " 'ts': '2016-09-21 18:04:30',\n", + " 'units': '',\n", + " 'value': 'NPT'},\n", + " 'records_per_buffer': {'__class__': 'qcodes.instrument_drivers.AlazarTech.ATS.AlazarParameter',\n", + " 'label': 'Records per Buffer',\n", + " 'name': 'records_per_buffer',\n", + " 'ts': '2016-09-21 18:04:30',\n", + " 'units': '',\n", + " 'value': 70},\n", + " 'sample_rate': {'__class__': 'qcodes.instrument_drivers.AlazarTech.ATS.AlazarParameter',\n", + " 'label': 'Sample Rate',\n", + " 'name': 'sample_rate',\n", + " 'ts': '2016-09-21 18:04:30',\n", + " 'units': 'S/s',\n", + " 'value': 100000000},\n", + " 'samples_per_record': {'__class__': 'qcodes.instrument_drivers.AlazarTech.ATS.AlazarParameter',\n", + " 'label': 'Samples per Record',\n", + " 'name': 'samples_per_record',\n", + " 'ts': '2016-09-21 18:04:30',\n", + " 'units': '',\n", + " 'value': 1024},\n", + " 'timeout_ticks': {'__class__': 'qcodes.instrument_drivers.AlazarTech.ATS.AlazarParameter',\n", + " 'label': 'Timeout Ticks',\n", + " 'name': 'timeout_ticks',\n", + " 'ts': '2016-09-21 18:04:30',\n", + " 'units': '10 us',\n", + " 'value': 0},\n", + " 'transfer_offset': {'__class__': 'qcodes.instrument_drivers.AlazarTech.ATS.AlazarParameter',\n", + " 'label': 'Transer Offset',\n", + " 'name': 'transfer_offset',\n", + " 'ts': '2016-09-21 18:04:30',\n", + " 'units': 'Samples',\n", + " 'value': 0},\n", + " 'trigger_delay': {'__class__': 'qcodes.instrument_drivers.AlazarTech.ATS.AlazarParameter',\n", + " 'label': 'Trigger Delay',\n", + " 'name': 'trigger_delay',\n", + " 'ts': '2016-09-21 18:04:30',\n", + " 'units': 'Sample clock cycles',\n", + " 'value': 0},\n", + " 'trigger_engine1': {'__class__': 'qcodes.instrument_drivers.AlazarTech.ATS.AlazarParameter',\n", + " 'label': 'Trigger Engine 1',\n", + " 'name': 'trigger_engine1',\n", + " 'ts': '2016-09-21 18:04:30',\n", + " 'units': '',\n", + " 'value': 'TRIG_ENGINE_K'},\n", + " 'trigger_engine2': {'__class__': 'qcodes.instrument_drivers.AlazarTech.ATS.AlazarParameter',\n", + " 'label': 'Trigger Engine 2',\n", + " 'name': 'trigger_engine2',\n", + " 'ts': '2016-09-21 18:04:30',\n", + " 'units': '',\n", + " 'value': 'TRIG_ENGINE_K'},\n", + " 'trigger_level1': {'__class__': 'qcodes.instrument_drivers.AlazarTech.ATS.AlazarParameter',\n", + " 'label': 'Trigger Level 1',\n", + " 'name': 'trigger_level1',\n", + " 'ts': '2016-09-21 18:04:30',\n", + " 'units': '',\n", + " 'value': 128},\n", + " 'trigger_level2': {'__class__': 'qcodes.instrument_drivers.AlazarTech.ATS.AlazarParameter',\n", + " 'label': 'Trigger Level 2',\n", + " 'name': 'trigger_level2',\n", + " 'ts': '2016-09-21 18:04:30',\n", + " 'units': '',\n", + " 'value': 128},\n", + " 'trigger_operation': {'__class__': 'qcodes.instrument_drivers.AlazarTech.ATS.AlazarParameter',\n", + " 'label': 'Trigger Operation',\n", + " 'name': 'trigger_operation',\n", + " 'ts': '2016-09-21 18:04:30',\n", + " 'units': '',\n", + " 'value': 'TRIG_ENGINE_OP_J'},\n", + " 'trigger_slope1': {'__class__': 'qcodes.instrument_drivers.AlazarTech.ATS.AlazarParameter',\n", + " 'label': 'Trigger Slope 1',\n", + " 'name': 'trigger_slope1',\n", + " 'ts': '2016-09-21 18:04:30',\n", + " 'units': '',\n", + " 'value': 'TRIG_SLOPE_POSITIVE'},\n", + " 'trigger_slope2': {'__class__': 'qcodes.instrument_drivers.AlazarTech.ATS.AlazarParameter',\n", + " 'label': 'Trigger Slope 2',\n", + " 'name': 'trigger_slope2',\n", + " 'ts': '2016-09-21 18:04:30',\n", + " 'units': '',\n", + " 'value': 'TRIG_SLOPE_POSITIVE'},\n", + " 'trigger_source1': {'__class__': 'qcodes.instrument_drivers.AlazarTech.ATS.AlazarParameter',\n", + " 'label': 'Trigger Source 1',\n", + " 'name': 'trigger_source1',\n", + " 'ts': '2016-09-21 18:04:30',\n", + " 'units': '',\n", + " 'value': 'EXTERNAL'},\n", + " 'trigger_source2': {'__class__': 'qcodes.instrument_drivers.AlazarTech.ATS.AlazarParameter',\n", + " 'label': 'Trigger Source 2',\n", + " 'name': 'trigger_source2',\n", + " 'ts': '2016-09-21 18:04:30',\n", + " 'units': '',\n", + " 'value': 'DISABLE'}}}" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# make a snapshot of the 'ats_inst' instrument\n", + "ats_inst.snapshot()" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "DataSet:\n", + " mode = DataMode.PULL_FROM_SERVER\n", + " location = '2016-09-21/18-04-30_AlazarTest'\n", + " | | | \n", + " Setpoint | dummy_set | dummy | (50,)\n", + " Measured | acquisition_controller_acquisition | acquisition | (50,)\n", + "started at 2016-09-21 18:04:31\n" + ] + }, + { + "data": { + "application/javascript": [ + "/* Put everything inside the global mpl namespace */\n", + "window.mpl = {};\n", + "\n", + "mpl.get_websocket_type = function() {\n", + " if (typeof(WebSocket) !== 'undefined') {\n", + " return WebSocket;\n", + " } else if (typeof(MozWebSocket) !== 'undefined') {\n", + " return MozWebSocket;\n", + " } else {\n", + " alert('Your browser does not have WebSocket support.' +\n", + " 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", + " 'Firefox 4 and 5 are also supported but you ' +\n", + " 'have to enable WebSockets in about:config.');\n", + " };\n", + "}\n", + "\n", + "mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n", + " this.id = figure_id;\n", + "\n", + " this.ws = websocket;\n", + "\n", + " this.supports_binary = (this.ws.binaryType != undefined);\n", + "\n", + " if (!this.supports_binary) {\n", + " var warnings = document.getElementById(\"mpl-warnings\");\n", + " if (warnings) {\n", + " warnings.style.display = 'block';\n", + " warnings.textContent = (\n", + " \"This browser does not support binary websocket messages. \" +\n", + " \"Performance may be slow.\");\n", + " }\n", + " }\n", + "\n", + " this.imageObj = new Image();\n", + "\n", + " this.context = undefined;\n", + " this.message = undefined;\n", + " this.canvas = undefined;\n", + " this.rubberband_canvas = undefined;\n", + " this.rubberband_context = undefined;\n", + " this.format_dropdown = undefined;\n", + "\n", + " this.image_mode = 'full';\n", + "\n", + " this.root = $('
');\n", + " this._root_extra_style(this.root)\n", + " this.root.attr('style', 'display: inline-block');\n", + "\n", + " $(parent_element).append(this.root);\n", + "\n", + " this._init_header(this);\n", + " this._init_canvas(this);\n", + " this._init_toolbar(this);\n", + "\n", + " var fig = this;\n", + "\n", + " this.waiting = false;\n", + "\n", + " this.ws.onopen = function () {\n", + " fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n", + " fig.send_message(\"send_image_mode\", {});\n", + " fig.send_message(\"refresh\", {});\n", + " }\n", + "\n", + " this.imageObj.onload = function() {\n", + " if (fig.image_mode == 'full') {\n", + " // Full images could contain transparency (where diff images\n", + " // almost always do), so we need to clear the canvas so that\n", + " // there is no ghosting.\n", + " fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", + " }\n", + " fig.context.drawImage(fig.imageObj, 0, 0);\n", + " };\n", + "\n", + " this.imageObj.onunload = function() {\n", + " this.ws.close();\n", + " }\n", + "\n", + " this.ws.onmessage = this._make_on_message_function(this);\n", + "\n", + " this.ondownload = ondownload;\n", + "}\n", + "\n", + "mpl.figure.prototype._init_header = function() {\n", + " var titlebar = $(\n", + " '
');\n", + " var titletext = $(\n", + " '
');\n", + " titlebar.append(titletext)\n", + " this.root.append(titlebar);\n", + " this.header = titletext[0];\n", + "}\n", + "\n", + "\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n", + "\n", + "}\n", + "\n", + "\n", + "mpl.figure.prototype._root_extra_style = function(canvas_div) {\n", + "\n", + "}\n", + "\n", + "mpl.figure.prototype._init_canvas = function() {\n", + " var fig = this;\n", + "\n", + " var canvas_div = $('
');\n", + "\n", + " canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n", + "\n", + " function canvas_keyboard_event(event) {\n", + " return fig.key_event(event, event['data']);\n", + " }\n", + "\n", + " canvas_div.keydown('key_press', canvas_keyboard_event);\n", + " canvas_div.keyup('key_release', canvas_keyboard_event);\n", + " this.canvas_div = canvas_div\n", + " this._canvas_extra_style(canvas_div)\n", + " this.root.append(canvas_div);\n", + "\n", + " var canvas = $('');\n", + " canvas.addClass('mpl-canvas');\n", + " canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n", + "\n", + " this.canvas = canvas[0];\n", + " this.context = canvas[0].getContext(\"2d\");\n", + "\n", + " var rubberband = $('');\n", + " rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n", + "\n", + " var pass_mouse_events = true;\n", + "\n", + " canvas_div.resizable({\n", + " start: function(event, ui) {\n", + " pass_mouse_events = false;\n", + " },\n", + " resize: function(event, ui) {\n", + " fig.request_resize(ui.size.width, ui.size.height);\n", + " },\n", + " stop: function(event, ui) {\n", + " pass_mouse_events = true;\n", + " fig.request_resize(ui.size.width, ui.size.height);\n", + " },\n", + " });\n", + "\n", + " function mouse_event_fn(event) {\n", + " if (pass_mouse_events)\n", + " return fig.mouse_event(event, event['data']);\n", + " }\n", + "\n", + " rubberband.mousedown('button_press', mouse_event_fn);\n", + " rubberband.mouseup('button_release', mouse_event_fn);\n", + " // Throttle sequential mouse events to 1 every 20ms.\n", + " rubberband.mousemove('motion_notify', mouse_event_fn);\n", + "\n", + " rubberband.mouseenter('figure_enter', mouse_event_fn);\n", + " rubberband.mouseleave('figure_leave', mouse_event_fn);\n", + "\n", + " canvas_div.on(\"wheel\", function (event) {\n", + " event = event.originalEvent;\n", + " event['data'] = 'scroll'\n", + " if (event.deltaY < 0) {\n", + " event.step = 1;\n", + " } else {\n", + " event.step = -1;\n", + " }\n", + " mouse_event_fn(event);\n", + " });\n", + "\n", + " canvas_div.append(canvas);\n", + " canvas_div.append(rubberband);\n", + "\n", + " this.rubberband = rubberband;\n", + " this.rubberband_canvas = rubberband[0];\n", + " this.rubberband_context = rubberband[0].getContext(\"2d\");\n", + " this.rubberband_context.strokeStyle = \"#000000\";\n", + "\n", + " this._resize_canvas = function(width, height) {\n", + " // Keep the size of the canvas, canvas container, and rubber band\n", + " // canvas in synch.\n", + " canvas_div.css('width', width)\n", + " canvas_div.css('height', height)\n", + "\n", + " canvas.attr('width', width);\n", + " canvas.attr('height', height);\n", + "\n", + " rubberband.attr('width', width);\n", + " rubberband.attr('height', height);\n", + " }\n", + "\n", + " // Set the figure to an initial 600x600px, this will subsequently be updated\n", + " // upon first draw.\n", + " this._resize_canvas(600, 600);\n", + "\n", + " // Disable right mouse context menu.\n", + " $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n", + " return false;\n", + " });\n", + "\n", + " function set_focus () {\n", + " canvas.focus();\n", + " canvas_div.focus();\n", + " }\n", + "\n", + " window.setTimeout(set_focus, 100);\n", + "}\n", + "\n", + "mpl.figure.prototype._init_toolbar = function() {\n", + " var fig = this;\n", + "\n", + " var nav_element = $('
')\n", + " nav_element.attr('style', 'width: 100%');\n", + " this.root.append(nav_element);\n", + "\n", + " // Define a callback function for later on.\n", + " function toolbar_event(event) {\n", + " return fig.toolbar_button_onclick(event['data']);\n", + " }\n", + " function toolbar_mouse_event(event) {\n", + " return fig.toolbar_button_onmouseover(event['data']);\n", + " }\n", + "\n", + " for(var toolbar_ind in mpl.toolbar_items) {\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) {\n", + " // put a spacer in here.\n", + " continue;\n", + " }\n", + " var button = $('