diff --git a/docs/examples/Qcodes example ATS_ONWORK.ipynb b/docs/examples/Qcodes example ATS_ONWORK.ipynb
new file mode 100644
index 00000000000..922d37e5816
--- /dev/null
+++ b/docs/examples/Qcodes example ATS_ONWORK.ipynb
@@ -0,0 +1,1659 @@
+{
+ "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",
+ " 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": [
+ "pyqtgraph plotting not supported, try \"from qcodes.plots.pyqtgraph import QtPlot\" to see the full error\n",
+ "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.9.11',\n",
+ " 'asopc_type': '2435577968',\n",
+ " 'driver_version': '5.9.11',\n",
+ " 'firmware': None,\n",
+ " 'latest_cal_date': '29-05-13',\n",
+ " 'memory_size': '4294966272',\n",
+ " 'model': 'ATS9870',\n",
+ " 'pcie_link_speed': '0.25GB/s',\n",
+ " 'pcie_link_width': '4',\n",
+ " 'serial': '910323',\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.DFT_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": true
+ },
+ "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.set_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=64,\n",
+ " #buffer_timeout=1000\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "0.024900543063357889"
+ ]
+ },
+ "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-08-16 14:21:37',\n",
+ " 'units': '',\n",
+ " 'value': {'CPLD_version': '13.8',\n",
+ " 'SDK_version': '5.9.11',\n",
+ " 'asopc_type': '2435577968',\n",
+ " 'driver_version': '5.9.11',\n",
+ " 'firmware': None,\n",
+ " 'latest_cal_date': '29-05-13',\n",
+ " 'memory_size': '4294966272',\n",
+ " 'model': 'ATS9870',\n",
+ " 'pcie_link_speed': '0.25GB/s',\n",
+ " 'pcie_link_width': '4',\n",
+ " 'serial': '910323',\n",
+ " 'vendor': 'AlazarTech'}},\n",
+ " 'alloc_buffers': {'__class__': 'qcodes.instrument_drivers.AlazarTech.ATS.AlazarParameter',\n",
+ " 'label': 'Alloc Buffers',\n",
+ " 'name': 'alloc_buffers',\n",
+ " 'ts': '2016-08-16 14:21:37',\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-08-16 14:21:37',\n",
+ " 'units': '',\n",
+ " 'value': 64},\n",
+ " 'buffer_timeout': {'__class__': 'qcodes.instrument_drivers.AlazarTech.ATS.AlazarParameter',\n",
+ " 'label': 'Buffer Timeout',\n",
+ " 'name': 'buffer_timeout',\n",
+ " 'ts': '2016-08-16 14:21:37',\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-08-16 14:21:37',\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-08-16 14:21:37',\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-08-16 14:21:37',\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-08-16 14:21:37',\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-08-16 14:21:37',\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-08-16 14:21:37',\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-08-16 14:21:37',\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-08-16 14:21:37',\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-08-16 14:21:37',\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-08-16 14:21:37',\n",
+ " 'units': '',\n",
+ " 'value': 'AC'},\n",
+ " 'decimation': {'__class__': 'qcodes.instrument_drivers.AlazarTech.ATS.AlazarParameter',\n",
+ " 'label': 'Decimation',\n",
+ " 'name': 'decimation',\n",
+ " 'ts': '2016-08-16 14:21:37',\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-08-16 14:21:37',\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-08-16 14:21:37',\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-08-16 14:21:37',\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-08-16 14:21:37',\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-08-16 14:21:37',\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-08-16 14:21:37',\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-08-16 14:21:37',\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-08-16 14:21:37',\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-08-16 14:21:37',\n",
+ " 'units': '',\n",
+ " 'value': 'DISABLED'},\n",
+ " 'mode': {'__class__': 'qcodes.instrument_drivers.AlazarTech.ATS.AlazarParameter',\n",
+ " 'label': 'Acquisiton mode',\n",
+ " 'name': 'mode',\n",
+ " 'ts': '2016-08-16 14:21:37',\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-08-16 14:21:37',\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-08-16 14:21:37',\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-08-16 14:21:37',\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-08-16 14:21:37',\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-08-16 14:21:37',\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-08-16 14:21:37',\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-08-16 14:21:37',\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-08-16 14:21:37',\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-08-16 14:21:37',\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-08-16 14:21:37',\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-08-16 14:21:37',\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-08-16 14:21:37',\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-08-16 14:21:37',\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-08-16 14:21:37',\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-08-16 14:21:37',\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-08-16/14-21-37_AlazarTest'\n",
+ " | | | \n",
+ " Setpoint | dummy_set | dummy | (50,)\n",
+ " Measured | acquisition_controller_acquisition | acquisition | (50,)\n",
+ "started at 2016-08-16 14:21:39\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 = $('');\n",
+ " button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n",
+ " 'ui-button-icon-only');\n",
+ " button.attr('role', 'button');\n",
+ " button.attr('aria-disabled', 'false');\n",
+ " button.click(method_name, toolbar_event);\n",
+ " button.mouseover(tooltip, toolbar_mouse_event);\n",
+ "\n",
+ " var icon_img = $('');\n",
+ " icon_img.addClass('ui-button-icon-primary ui-icon');\n",
+ " icon_img.addClass(image);\n",
+ " icon_img.addClass('ui-corner-all');\n",
+ "\n",
+ " var tooltip_span = $('');\n",
+ " tooltip_span.addClass('ui-button-text');\n",
+ " tooltip_span.html(tooltip);\n",
+ "\n",
+ " button.append(icon_img);\n",
+ " button.append(tooltip_span);\n",
+ "\n",
+ " nav_element.append(button);\n",
+ " }\n",
+ "\n",
+ " var fmt_picker_span = $('');\n",
+ "\n",
+ " var fmt_picker = $('');\n",
+ " fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n",
+ " fmt_picker_span.append(fmt_picker);\n",
+ " nav_element.append(fmt_picker_span);\n",
+ " this.format_dropdown = fmt_picker[0];\n",
+ "\n",
+ " for (var ind in mpl.extensions) {\n",
+ " var fmt = mpl.extensions[ind];\n",
+ " var option = $(\n",
+ " '', {selected: fmt === mpl.default_extension}).html(fmt);\n",
+ " fmt_picker.append(option)\n",
+ " }\n",
+ "\n",
+ " // Add hover states to the ui-buttons\n",
+ " $( \".ui-button\" ).hover(\n",
+ " function() { $(this).addClass(\"ui-state-hover\");},\n",
+ " function() { $(this).removeClass(\"ui-state-hover\");}\n",
+ " );\n",
+ "\n",
+ " var status_bar = $('');\n",
+ " nav_element.append(status_bar);\n",
+ " this.message = status_bar[0];\n",
+ "}\n",
+ "\n",
+ "mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n",
+ " // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n",
+ " // which will in turn request a refresh of the image.\n",
+ " this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n",
+ "}\n",
+ "\n",
+ "mpl.figure.prototype.send_message = function(type, properties) {\n",
+ " properties['type'] = type;\n",
+ " properties['figure_id'] = this.id;\n",
+ " this.ws.send(JSON.stringify(properties));\n",
+ "}\n",
+ "\n",
+ "mpl.figure.prototype.send_draw_message = function() {\n",
+ " if (!this.waiting) {\n",
+ " this.waiting = true;\n",
+ " this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n",
+ " }\n",
+ "}\n",
+ "\n",
+ "\n",
+ "mpl.figure.prototype.handle_save = function(fig, msg) {\n",
+ " var format_dropdown = fig.format_dropdown;\n",
+ " var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n",
+ " fig.ondownload(fig, format);\n",
+ "}\n",
+ "\n",
+ "\n",
+ "mpl.figure.prototype.handle_resize = function(fig, msg) {\n",
+ " var size = msg['size'];\n",
+ " if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n",
+ " fig._resize_canvas(size[0], size[1]);\n",
+ " fig.send_message(\"refresh\", {});\n",
+ " };\n",
+ "}\n",
+ "\n",
+ "mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n",
+ " var x0 = msg['x0'];\n",
+ " var y0 = fig.canvas.height - msg['y0'];\n",
+ " var x1 = msg['x1'];\n",
+ " var y1 = fig.canvas.height - msg['y1'];\n",
+ " x0 = Math.floor(x0) + 0.5;\n",
+ " y0 = Math.floor(y0) + 0.5;\n",
+ " x1 = Math.floor(x1) + 0.5;\n",
+ " y1 = Math.floor(y1) + 0.5;\n",
+ " var min_x = Math.min(x0, x1);\n",
+ " var min_y = Math.min(y0, y1);\n",
+ " var width = Math.abs(x1 - x0);\n",
+ " var height = Math.abs(y1 - y0);\n",
+ "\n",
+ " fig.rubberband_context.clearRect(\n",
+ " 0, 0, fig.canvas.width, fig.canvas.height);\n",
+ "\n",
+ " fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n",
+ "}\n",
+ "\n",
+ "mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n",
+ " // Updates the figure title.\n",
+ " fig.header.textContent = msg['label'];\n",
+ "}\n",
+ "\n",
+ "mpl.figure.prototype.handle_cursor = function(fig, msg) {\n",
+ " var cursor = msg['cursor'];\n",
+ " switch(cursor)\n",
+ " {\n",
+ " case 0:\n",
+ " cursor = 'pointer';\n",
+ " break;\n",
+ " case 1:\n",
+ " cursor = 'default';\n",
+ " break;\n",
+ " case 2:\n",
+ " cursor = 'crosshair';\n",
+ " break;\n",
+ " case 3:\n",
+ " cursor = 'move';\n",
+ " break;\n",
+ " }\n",
+ " fig.rubberband_canvas.style.cursor = cursor;\n",
+ "}\n",
+ "\n",
+ "mpl.figure.prototype.handle_message = function(fig, msg) {\n",
+ " fig.message.textContent = msg['message'];\n",
+ "}\n",
+ "\n",
+ "mpl.figure.prototype.handle_draw = function(fig, msg) {\n",
+ " // Request the server to send over a new figure.\n",
+ " fig.send_draw_message();\n",
+ "}\n",
+ "\n",
+ "mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n",
+ " fig.image_mode = msg['mode'];\n",
+ "}\n",
+ "\n",
+ "mpl.figure.prototype.updated_canvas_event = function() {\n",
+ " // Called whenever the canvas gets updated.\n",
+ " this.send_message(\"ack\", {});\n",
+ "}\n",
+ "\n",
+ "// A function to construct a web socket function for onmessage handling.\n",
+ "// Called in the figure constructor.\n",
+ "mpl.figure.prototype._make_on_message_function = function(fig) {\n",
+ " return function socket_on_message(evt) {\n",
+ " if (evt.data instanceof Blob) {\n",
+ " /* FIXME: We get \"Resource interpreted as Image but\n",
+ " * transferred with MIME type text/plain:\" errors on\n",
+ " * Chrome. But how to set the MIME type? It doesn't seem\n",
+ " * to be part of the websocket stream */\n",
+ " evt.data.type = \"image/png\";\n",
+ "\n",
+ " /* Free the memory for the previous frames */\n",
+ " if (fig.imageObj.src) {\n",
+ " (window.URL || window.webkitURL).revokeObjectURL(\n",
+ " fig.imageObj.src);\n",
+ " }\n",
+ "\n",
+ " fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n",
+ " evt.data);\n",
+ " fig.updated_canvas_event();\n",
+ " fig.waiting = false;\n",
+ " return;\n",
+ " }\n",
+ " else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n",
+ " fig.imageObj.src = evt.data;\n",
+ " fig.updated_canvas_event();\n",
+ " fig.waiting = false;\n",
+ " return;\n",
+ " }\n",
+ "\n",
+ " var msg = JSON.parse(evt.data);\n",
+ " var msg_type = msg['type'];\n",
+ "\n",
+ " // Call the \"handle_{type}\" callback, which takes\n",
+ " // the figure and JSON message as its only arguments.\n",
+ " try {\n",
+ " var callback = fig[\"handle_\" + msg_type];\n",
+ " } catch (e) {\n",
+ " console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n",
+ " return;\n",
+ " }\n",
+ "\n",
+ " if (callback) {\n",
+ " try {\n",
+ " // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n",
+ " callback(fig, msg);\n",
+ " } catch (e) {\n",
+ " console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n",
+ " }\n",
+ " }\n",
+ " };\n",
+ "}\n",
+ "\n",
+ "// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n",
+ "mpl.findpos = function(e) {\n",
+ " //this section is from http://www.quirksmode.org/js/events_properties.html\n",
+ " var targ;\n",
+ " if (!e)\n",
+ " e = window.event;\n",
+ " if (e.target)\n",
+ " targ = e.target;\n",
+ " else if (e.srcElement)\n",
+ " targ = e.srcElement;\n",
+ " if (targ.nodeType == 3) // defeat Safari bug\n",
+ " targ = targ.parentNode;\n",
+ "\n",
+ " // jQuery normalizes the pageX and pageY\n",
+ " // pageX,Y are the mouse positions relative to the document\n",
+ " // offset() returns the position of the element relative to the document\n",
+ " var x = e.pageX - $(targ).offset().left;\n",
+ " var y = e.pageY - $(targ).offset().top;\n",
+ "\n",
+ " return {\"x\": x, \"y\": y};\n",
+ "};\n",
+ "\n",
+ "/*\n",
+ " * return a copy of an object with only non-object keys\n",
+ " * we need this to avoid circular references\n",
+ " * http://stackoverflow.com/a/24161582/3208463\n",
+ " */\n",
+ "function simpleKeys (original) {\n",
+ " return Object.keys(original).reduce(function (obj, key) {\n",
+ " if (typeof original[key] !== 'object')\n",
+ " obj[key] = original[key]\n",
+ " return obj;\n",
+ " }, {});\n",
+ "}\n",
+ "\n",
+ "mpl.figure.prototype.mouse_event = function(event, name) {\n",
+ " var canvas_pos = mpl.findpos(event)\n",
+ "\n",
+ " if (name === 'button_press')\n",
+ " {\n",
+ " this.canvas.focus();\n",
+ " this.canvas_div.focus();\n",
+ " }\n",
+ "\n",
+ " var x = canvas_pos.x;\n",
+ " var y = canvas_pos.y;\n",
+ "\n",
+ " this.send_message(name, {x: x, y: y, button: event.button,\n",
+ " step: event.step,\n",
+ " guiEvent: simpleKeys(event)});\n",
+ "\n",
+ " /* This prevents the web browser from automatically changing to\n",
+ " * the text insertion cursor when the button is pressed. We want\n",
+ " * to control all of the cursor setting manually through the\n",
+ " * 'cursor' event from matplotlib */\n",
+ " event.preventDefault();\n",
+ " return false;\n",
+ "}\n",
+ "\n",
+ "mpl.figure.prototype._key_event_extra = function(event, name) {\n",
+ " // Handle any extra behaviour associated with a key event\n",
+ "}\n",
+ "\n",
+ "mpl.figure.prototype.key_event = function(event, name) {\n",
+ "\n",
+ " // Prevent repeat events\n",
+ " if (name == 'key_press')\n",
+ " {\n",
+ " if (event.which === this._key)\n",
+ " return;\n",
+ " else\n",
+ " this._key = event.which;\n",
+ " }\n",
+ " if (name == 'key_release')\n",
+ " this._key = null;\n",
+ "\n",
+ " var value = '';\n",
+ " if (event.ctrlKey && event.which != 17)\n",
+ " value += \"ctrl+\";\n",
+ " if (event.altKey && event.which != 18)\n",
+ " value += \"alt+\";\n",
+ " if (event.shiftKey && event.which != 16)\n",
+ " value += \"shift+\";\n",
+ "\n",
+ " value += 'k';\n",
+ " value += event.which.toString();\n",
+ "\n",
+ " this._key_event_extra(event, name);\n",
+ "\n",
+ " this.send_message(name, {key: value,\n",
+ " guiEvent: simpleKeys(event)});\n",
+ " return false;\n",
+ "}\n",
+ "\n",
+ "mpl.figure.prototype.toolbar_button_onclick = function(name) {\n",
+ " if (name == 'download') {\n",
+ " this.handle_save(this, null);\n",
+ " } else {\n",
+ " this.send_message(\"toolbar_button\", {name: name});\n",
+ " }\n",
+ "};\n",
+ "\n",
+ "mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n",
+ " this.message.textContent = tooltip;\n",
+ "};\n",
+ "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n",
+ "\n",
+ "mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n",
+ "\n",
+ "mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n",
+ " // Create a \"websocket\"-like object which calls the given IPython comm\n",
+ " // object with the appropriate methods. Currently this is a non binary\n",
+ " // socket, so there is still some room for performance tuning.\n",
+ " var ws = {};\n",
+ "\n",
+ " ws.close = function() {\n",
+ " comm.close()\n",
+ " };\n",
+ " ws.send = function(m) {\n",
+ " //console.log('sending', m);\n",
+ " comm.send(m);\n",
+ " };\n",
+ " // Register the callback with on_msg.\n",
+ " comm.on_msg(function(msg) {\n",
+ " //console.log('receiving', msg['content']['data'], msg);\n",
+ " // Pass the mpl event to the overriden (by mpl) onmessage function.\n",
+ " ws.onmessage(msg['content']['data'])\n",
+ " });\n",
+ " return ws;\n",
+ "}\n",
+ "\n",
+ "mpl.mpl_figure_comm = function(comm, msg) {\n",
+ " // This is the function which gets called when the mpl process\n",
+ " // starts-up an IPython Comm through the \"matplotlib\" channel.\n",
+ "\n",
+ " var id = msg.content.data.id;\n",
+ " // Get hold of the div created by the display call when the Comm\n",
+ " // socket was opened in Python.\n",
+ " var element = $(\"#\" + id);\n",
+ " var ws_proxy = comm_websocket_adapter(comm)\n",
+ "\n",
+ " function ondownload(figure, format) {\n",
+ " window.open(figure.imageObj.src);\n",
+ " }\n",
+ "\n",
+ " var fig = new mpl.figure(id, ws_proxy,\n",
+ " ondownload,\n",
+ " element.get(0));\n",
+ "\n",
+ " // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n",
+ " // web socket which is closed, not our websocket->open comm proxy.\n",
+ " ws_proxy.onopen();\n",
+ "\n",
+ " fig.parent_element = element.get(0);\n",
+ " fig.cell_info = mpl.find_output_cell(\"\");\n",
+ " if (!fig.cell_info) {\n",
+ " console.error(\"Failed to find cell for figure\", id, fig);\n",
+ " return;\n",
+ " }\n",
+ "\n",
+ " var output_index = fig.cell_info[2]\n",
+ " var cell = fig.cell_info[0];\n",
+ "\n",
+ "};\n",
+ "\n",
+ "mpl.figure.prototype.handle_close = function(fig, msg) {\n",
+ " fig.root.unbind('remove')\n",
+ "\n",
+ " // Update the output cell to use the data from the current canvas.\n",
+ " fig.push_to_output();\n",
+ " var dataURL = fig.canvas.toDataURL();\n",
+ " // Re-enable the keyboard manager in IPython - without this line, in FF,\n",
+ " // the notebook keyboard shortcuts fail.\n",
+ " IPython.keyboard_manager.enable()\n",
+ " $(fig.parent_element).html('');\n",
+ " fig.close_ws(fig, msg);\n",
+ "}\n",
+ "\n",
+ "mpl.figure.prototype.close_ws = function(fig, msg){\n",
+ " fig.send_message('closing', msg);\n",
+ " // fig.ws.close()\n",
+ "}\n",
+ "\n",
+ "mpl.figure.prototype.push_to_output = function(remove_interactive) {\n",
+ " // Turn the data on the canvas into data in the output cell.\n",
+ " var dataURL = this.canvas.toDataURL();\n",
+ " this.cell_info[1]['text/html'] = '';\n",
+ "}\n",
+ "\n",
+ "mpl.figure.prototype.updated_canvas_event = function() {\n",
+ " // Tell IPython that the notebook contents must change.\n",
+ " IPython.notebook.set_dirty(true);\n",
+ " this.send_message(\"ack\", {});\n",
+ " var fig = this;\n",
+ " // Wait a second, then push the new image to the DOM so\n",
+ " // that it is saved nicely (might be nice to debounce this).\n",
+ " setTimeout(function () { fig.push_to_output() }, 1000);\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) { 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('Stop Interaction', toolbar_mouse_event);\n",
+ " buttongrp.append(button);\n",
+ " var titlebar = this.root.find($('.ui-dialog-titlebar'));\n",
+ " titlebar.prepend(buttongrp);\n",
+ "}\n",
+ "\n",
+ "mpl.figure.prototype._root_extra_style = function(el){\n",
+ " var fig = this\n",
+ " el.on(\"remove\", function(){\n",
+ "\tfig.close_ws(fig, {});\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": 9,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "# Finally show that this instrument also works within a loop\n",
+ "dummy = parameter.ManualParameter(name=\"dummy\")\n",
+ "data = qc.Loop(dummy[0:50:1]).each(acquisition_controller.acquisition).run(name='AlazarTest')\n",
+ "qc.MatPlot(data.acquisition_controller_acquisition)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": true
+ },
+ "outputs": [],
+ "source": []
+ }
+ ],
+ "metadata": {
+ "anaconda-cloud": {},
+ "kernelspec": {
+ "display_name": "Python 3",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.5.1"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/qcodes/instrument/base.py b/qcodes/instrument/base.py
index 546750d4054..c5ba51b1c70 100644
--- a/qcodes/instrument/base.py
+++ b/qcodes/instrument/base.py
@@ -81,7 +81,7 @@ def __new__(cls, *args, server_name='', **kwargs):
return RemoteInstrument(*args, instrument_class=cls,
server_name=server_name, **kwargs)
- def __init__(self, name, server_name=None, **kwargs):
+ def __init__(self, name, server_name=None, server=None, **kwargs):
self._t0 = time.time()
super().__init__(**kwargs)
self.parameters = {}
diff --git a/qcodes/instrument/server.py b/qcodes/instrument/server.py
index 39b3f5a71f1..8c35067ab70 100644
--- a/qcodes/instrument/server.py
+++ b/qcodes/instrument/server.py
@@ -154,7 +154,7 @@ def handle_new(self, instrument_class, new_id, *args, **kwargs):
for key, value in self._shared_attrs.items():
if key in instrument_class.shared_kwargs:
kwargs[key] = value
- ins = instrument_class(*args, server_name=None, **kwargs)
+ ins = instrument_class(*args, server_name=None, server=self, **kwargs)
self.instruments[new_id] = ins
diff --git a/qcodes/instrument_drivers/AlazarTech/ATS.py b/qcodes/instrument_drivers/AlazarTech/ATS.py
new file mode 100644
index 00000000000..4fea97e06d9
--- /dev/null
+++ b/qcodes/instrument_drivers/AlazarTech/ATS.py
@@ -0,0 +1,854 @@
+import ctypes
+import logging
+import numpy as np
+import os
+
+from qcodes.instrument.base import Instrument
+from qcodes.instrument.parameter import Parameter
+from qcodes.utils import validators
+
+# TODO(damazter) (C) logging
+
+# 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?
+
+
+class AlazarTech_ATS(Instrument):
+ # override dll_path in your init script or in the board constructor
+ # if you have it somewhere else
+ dll_path = 'C:\\WINDOWS\\System32\\ATSApi'
+
+ # override channels in a subclass if needed
+ channels = 2
+
+ _success = 512
+
+ _error_codes = {
+ 513: 'ApiFailed',
+ 514: 'ApiAccessDenied',
+ 515: 'ApiDmaChannelUnavailable',
+ 516: 'ApiDmaChannelInvalid',
+ 517: 'ApiDmaChannelTypeError',
+ 518: 'ApiDmaInProgress',
+ 519: 'ApiDmaDone',
+ 520: 'ApiDmaPaused',
+ 521: 'ApiDmaNotPaused',
+ 522: 'ApiDmaCommandInvalid',
+ 523: 'ApiDmaManReady',
+ 524: 'ApiDmaManNotReady',
+ 525: 'ApiDmaInvalidChannelPriority',
+ 526: 'ApiDmaManCorrupted',
+ 527: 'ApiDmaInvalidElementIndex',
+ 528: 'ApiDmaNoMoreElements',
+ 529: 'ApiDmaSglInvalid',
+ 530: 'ApiDmaSglQueueFull',
+ 531: 'ApiNullParam',
+ 532: 'ApiInvalidBusIndex',
+ 533: 'ApiUnsupportedFunction',
+ 534: 'ApiInvalidPciSpace',
+ 535: 'ApiInvalidIopSpace',
+ 536: 'ApiInvalidSize',
+ 537: 'ApiInvalidAddress',
+ 538: 'ApiInvalidAccessType',
+ 539: 'ApiInvalidIndex',
+ 540: 'ApiMuNotReady',
+ 541: 'ApiMuFifoEmpty',
+ 542: 'ApiMuFifoFull',
+ 543: 'ApiInvalidRegister',
+ 544: 'ApiDoorbellClearFailed',
+ 545: 'ApiInvalidUserPin',
+ 546: 'ApiInvalidUserState',
+ 547: 'ApiEepromNotPresent',
+ 548: 'ApiEepromTypeNotSupported',
+ 549: 'ApiEepromBlank',
+ 550: 'ApiConfigAccessFailed',
+ 551: 'ApiInvalidDeviceInfo',
+ 552: 'ApiNoActiveDriver',
+ 553: 'ApiInsufficientResources',
+ 554: 'ApiObjectAlreadyAllocated',
+ 555: 'ApiAlreadyInitialized',
+ 556: 'ApiNotInitialized',
+ 557: 'ApiBadConfigRegEndianMode',
+ 558: 'ApiInvalidPowerState',
+ 559: 'ApiPowerDown',
+ 560: 'ApiFlybyNotSupported',
+ 561: 'ApiNotSupportThisChannel',
+ 562: 'ApiNoAction',
+ 563: 'ApiHSNotSupported',
+ 564: 'ApiVPDNotSupported',
+ 565: 'ApiVpdNotEnabled',
+ 566: 'ApiNoMoreCap',
+ 567: 'ApiInvalidOffset',
+ 568: 'ApiBadPinDirection',
+ 569: 'ApiPciTimeout',
+ 570: 'ApiDmaChannelClosed',
+ 571: 'ApiDmaChannelError',
+ 572: 'ApiInvalidHandle',
+ 573: 'ApiBufferNotReady',
+ 574: 'ApiInvalidData',
+ 575: 'ApiDoNothing',
+ 576: 'ApiDmaSglBuildFailed',
+ 577: 'ApiPMNotSupported',
+ 578: 'ApiInvalidDriverVersion',
+ 579: ('ApiWaitTimeout: operation did not finish during '
+ 'timeout interval. Check your trigger.'),
+ 580: 'ApiWaitCanceled',
+ 581: 'ApiBufferTooSmall',
+ 582: ('ApiBufferOverflow:rate of acquiring data > rate of '
+ 'transferring data to local memory. Try reducing sample rate, '
+ 'reducing number of enabled channels, increasing size of each '
+ 'DMA buffer or increase number of DMA buffers.'),
+ 583: 'ApiInvalidBuffer',
+ 584: 'ApiInvalidRecordsPerBuffer',
+ 585: ('ApiDmaPending:Async I/O operation was successfully started, '
+ 'it will be completed when sufficient trigger events are '
+ 'supplied to fill the buffer.'),
+ 586: ('ApiLockAndProbePagesFailed:Driver or operating system was '
+ 'unable to prepare the specified buffer for DMA transfer. '
+ 'Try reducing buffer size or total number of buffers.'),
+ 587: 'ApiWaitAbandoned',
+ 588: 'ApiWaitFailed',
+ 589: ('ApiTransferComplete:This buffer is last in the current '
+ 'acquisition.'),
+ 590: 'ApiPllNotLocked:hardware error, contact AlazarTech',
+ 591: ('ApiNotSupportedInDualChannelMode:Requested number of samples '
+ 'per channel is too large to fit in on-board memory. Try '
+ 'reducing number of samples per channel, or switch to '
+ 'single channel mode.')
+ }
+
+ _board_names = {
+ 1: 'ATS850',
+ 2: 'ATS310',
+ 3: 'ATS330',
+ 4: 'ATS855',
+ 5: 'ATS315',
+ 6: 'ATS335',
+ 7: 'ATS460',
+ 8: 'ATS860',
+ 9: 'ATS660',
+ 10: 'ATS665',
+ 11: 'ATS9462',
+ 12: 'ATS9434',
+ 13: 'ATS9870',
+ 14: 'ATS9350',
+ 15: 'ATS9325',
+ 16: 'ATS9440',
+ 17: 'ATS9410',
+ 18: 'ATS9351',
+ 19: 'ATS9310',
+ 20: 'ATS9461',
+ 21: 'ATS9850',
+ 22: 'ATS9625',
+ 23: 'ATG6500',
+ 24: 'ATS9626',
+ 25: 'ATS9360',
+ 26: 'AXI9870',
+ 27: 'ATS9370',
+ 28: 'ATU7825',
+ 29: 'ATS9373',
+ 30: 'ATS9416'
+ }
+
+ @classmethod
+ def find_boards(cls, dll_path=None):
+ dll = ctypes.cdll.LoadLibrary(dll_path or cls.dll_path)
+
+ system_count = dll.AlazarNumOfSystems()
+ boards = []
+ for system_id in range(1, system_count + 1):
+ board_count = dll.AlazarBoardsInSystemBySystemID(system_id)
+ for board_id in range(1, board_count + 1):
+ boards.append(cls.get_board_info(dll, system_id, board_id))
+ return boards
+
+ @classmethod
+ def get_board_info(cls, dll, system_id, board_id):
+ # make a temporary instrument for this board, to make it easier
+ # to get its info
+ board = cls('temp', system_id=system_id, board_id=board_id,
+ server_name=None)
+ handle = board._handle
+ board_kind = cls._board_names[dll.AlazarGetBoardKind(handle)]
+
+ max_s, bps = board._get_channel_info(handle)
+ return {
+ 'system_id': system_id,
+ 'board_id': board_id,
+ 'board_kind': board_kind,
+ 'max_samples': max_s,
+ 'bits_per_sample': bps
+ }
+
+ 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._handle = self._ATS_dll.AlazarGetBoardBySystemID(system_id,
+ board_id)
+ if not self._handle:
+ raise Exception('AlazarTech_ATS not found at '
+ 'system {}, board {}'.format(system_id, board_id))
+
+ self.buffer_list = []
+
+ def get_idn(self):
+ board_kind = self._board_names[
+ self._ATS_dll.AlazarGetBoardKind(self._handle)]
+
+ major = np.array([0], dtype=np.uint8)
+ minor = np.array([0], dtype=np.uint8)
+ revision = np.array([0], dtype=np.uint8)
+ self._call_dll('AlazarGetCPLDVersion',
+ self._handle,
+ major.ctypes.data,
+ minor.ctypes.data)
+ cpld_ver = str(major[0])+"."+str(minor[0])
+
+ self._call_dll('AlazarGetDriverVersion',
+ major.ctypes.data,
+ minor.ctypes.data,
+ revision.ctypes.data)
+ driver_ver = str(major[0])+"."+str(minor[0])+"."+str(revision[0])
+
+ self._call_dll('AlazarGetSDKVersion',
+ major.ctypes.data,
+ minor.ctypes.data,
+ revision.ctypes.data)
+ sdk_ver = str(major[0])+"."+str(minor[0])+"."+str(revision[0])
+
+ value = np.array([0], dtype=np.uint32)
+ self._call_dll('AlazarQueryCapability',
+ self._handle, 0x10000024, 0, value.ctypes.data)
+ serial = str(value[0])
+ self._call_dll('AlazarQueryCapability',
+ self._handle, 0x10000026, 0, value.ctypes.data)
+ latest_cal_date = (str(value[0])[0:2] + "-" +
+ str(value[0])[2:4] + "-" +
+ str(value[0])[4:6])
+
+ self._call_dll('AlazarQueryCapability',
+ self._handle, 0x1000002A, 0, value.ctypes.data)
+ memory_size = str(value[0])
+ self._call_dll('AlazarQueryCapability',
+ self._handle, 0x1000002C, 0, value.ctypes.data)
+ asopc_type = str(value[0])
+
+ # see the ATS-SDK programmer's guide
+ # about the encoding of the link speed
+ self._call_dll('AlazarQueryCapability',
+ self._handle, 0x10000030, 0, value.ctypes.data)
+ pcie_link_speed = str(value[0]*2.5/10)+"GB/s"
+ self._call_dll('AlazarQueryCapability',
+ self._handle, 0x10000031, 0, value.ctypes.data)
+ pcie_link_width = str(value[0])
+
+
+ return {'firmware': None,
+ 'model': board_kind,
+ 'serial': serial,
+ 'vendor': 'AlazarTech',
+ 'CPLD_version': cpld_ver,
+ 'driver_version': driver_ver,
+ 'SDK_version': sdk_ver,
+ 'latest_cal_date': latest_cal_date,
+ 'memory_size': memory_size,
+ 'asopc_type': asopc_type,
+ 'pcie_link_speed': pcie_link_speed,
+ 'pcie_link_width': pcie_link_width}
+
+ def config(self, clock_source=None, sample_rate=None, clock_edge=None,
+ decimation=None, coupling=None, channel_range=None,
+ impedance=None, bwlimit=None, trigger_operation=None,
+ trigger_engine1=None, trigger_source1=None,
+ trigger_slope1=None, trigger_level1=None,
+ trigger_engine2=None, trigger_source2=None,
+ trigger_slope2=None, trigger_level2=None,
+ external_trigger_coupling=None, external_trigger_range=None,
+ trigger_delay=None, timeout_ticks=None):
+ """
+
+ :param clock_source:
+ :param sample_rate:
+ :param clock_edge:
+ :param decimation:
+ :param coupling:
+ :param channel_range:
+ :param impedance:
+ :param bwlimit:
+ :param trigger_operation:
+ :param trigger_engine1:
+ :param trigger_source1:
+ :param trigger_slope1:
+ :param trigger_level1:
+ :param trigger_engine2:
+ :param trigger_source2:
+ :param trigger_slope2:
+ :param trigger_level2:
+ :param external_trigger_coupling:
+ :param external_trigger_range:
+ :param trigger_delay:
+ :param timeout_ticks:
+ :return:
+ """
+ # region set parameters from args
+
+ self._set_if_present('clock_source', clock_source)
+ self._set_if_present('sample_rate', sample_rate)
+ self._set_if_present('clock_edge', clock_edge)
+ self._set_if_present('decimation', decimation)
+
+ self._set_list_if_present('coupling', coupling)
+ self._set_list_if_present('channel_range', channel_range)
+ self._set_list_if_present('impedance', impedance)
+ self._set_list_if_present('bwlimit', bwlimit)
+
+ self._set_if_present('trigger_operation', trigger_operation)
+ self._set_if_present('trigger_engine1', trigger_engine1)
+ self._set_if_present('trigger_source1', trigger_source1)
+ self._set_if_present('trigger_slope1', trigger_slope1)
+ self._set_if_present('trigger_level1', trigger_level1)
+
+ self._set_if_present('trigger_engine2', trigger_engine2)
+ self._set_if_present('trigger_source2', trigger_source2)
+ self._set_if_present('trigger_slope2', trigger_slope2)
+ self._set_if_present('trigger_level2', trigger_level2)
+
+ self._set_if_present('external_trigger_coupling',
+ external_trigger_coupling)
+ self._set_if_present('external_trigger_range',
+ external_trigger_range)
+ self._set_if_present('trigger_delay', trigger_delay)
+ self._set_if_present('timeout_ticks', timeout_ticks)
+ # endregion
+
+ self._call_dll('AlazarSetCaptureClock',
+ self._handle, self.clock_source, self.sample_rate,
+ self.clock_edge, self.decimation)
+
+ for i in range(1, self.channels + 1):
+ self._call_dll('AlazarInputControl',
+ self._handle, i,
+ 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)])
+
+ self._call_dll('AlazarSetTriggerOperation',
+ self._handle, self.trigger_operation,
+ self.trigger_engine1, self.trigger_source1,
+ self.trigger_slope1, self.trigger_level1,
+ self.trigger_engine2, self.trigger_source2,
+ self.trigger_slope2, self.trigger_level2)
+
+ self._call_dll('AlazarSetExternalTrigger',
+ self._handle, self.external_trigger_coupling,
+ self.external_trigger_range)
+
+ self._call_dll('AlazarSetTriggerDelay',
+ self._handle, self.trigger_delay)
+
+ self._call_dll('AlazarSetTriggerTimeOut',
+ self._handle, self.timeout_ticks)
+
+ # TODO(damazter) (W) config AUXIO
+
+ def _get_channel_info(self, handle):
+ bps = np.array([0], dtype=np.uint8) # bps bits per sample
+ max_s = np.array([0], dtype=np.uint32) # max_s memory size in samples
+ self._call_dll('AlazarGetChannelInfo',
+ handle, max_s.ctypes.data, bps.ctypes.data)
+ return max_s[0], bps[0]
+
+ def acquire(self, mode=None, samples_per_record=None,
+ records_per_buffer=None, buffers_per_acquisition=None,
+ channel_selection=None, transfer_offset=None,
+ external_startcapture=None, enable_record_headers=None,
+ alloc_buffers=None, fifo_only_streaming=None,
+ interleave_samples=None, get_processed_data=None,
+ allocated_buffers=None, buffer_timeout=None,
+ acquisition_controller=None):
+ """
+
+ :param mode:
+ :param samples_per_record:
+ :param records_per_buffer:
+ :param buffers_per_acquisition:
+ :param channel_selection:
+ :param transfer_offset:
+ :param external_startcapture:
+ :param enable_record_headers:
+ :param alloc_buffers:
+ :param fifo_only_streaming:
+ :param interleave_samples:
+ :param get_processed_data:
+ :param allocated_buffers:
+ :param buffer_timeout:
+ :param acquisition_controller:
+ :return:
+ """
+ # region set parameters from args
+ self._set_if_present('mode', mode)
+ 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)
+ self._set_if_present('channel_selection', channel_selection)
+ self._set_if_present('transfer_offset', transfer_offset)
+ self._set_if_present('external_startcapture', external_startcapture)
+ self._set_if_present('enable_record_headers', enable_record_headers)
+ self._set_if_present('alloc_buffers', alloc_buffers)
+ self._set_if_present('fifo_only_streaming', fifo_only_streaming)
+ self._set_if_present('interleave_samples', interleave_samples)
+ self._set_if_present('get_processed_data', get_processed_data)
+ self._set_if_present('allocated_buffers', allocated_buffers)
+ self._set_if_present('buffer_timeout', buffer_timeout)
+
+ # endregion
+ self.mode._set_updated()
+ mode = self.mode.get()
+ if mode not in ('TS', 'NPT'):
+ raise Exception("Only the 'TS' and 'NPT' modes are implemented "
+ "at this point")
+
+ # -----set final configurations-----
+
+ # 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
+ post_trigger_size = self.samples_per_record._get_byte()
+ self._call_dll('AlazarSetRecordSize',
+ self._handle, pretriggersize,
+ 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()
+ acquire_flags = (self.mode._get_byte() |
+ self.external_startcapture._get_byte() |
+ self.enable_record_headers._get_byte() |
+ self.alloc_buffers._get_byte() |
+ self.fifo_only_streaming._get_byte() |
+ self.interleave_samples._get_byte() |
+ self.get_processed_data._get_byte())
+
+ if mode == 'NPT':
+ records_per_buffer = self.records_per_buffer._get_byte()
+ records_per_acquisition = (
+ records_per_buffer * buffers_per_acquisition)
+ samples_per_buffer = samples_per_record * records_per_buffer
+
+ self._call_dll('AlazarBeforeAsyncRead',
+ self._handle, self.channel_selection,
+ self.transfer_offset, samples_per_record,
+ records_per_buffer, records_per_acquisition,
+ acquire_flags)
+
+ elif mode == 'TS':
+ if (samples_per_record % buffers_per_acquisition != 0):
+ logging.warning('buffers_per_acquisition is not a divisor of '
+ 'samples per record which it should be in '
+ 'TS mode, rounding down in samples per buffer '
+ 'calculation')
+ samples_per_buffer = int(samples_per_record /
+ buffers_per_acquisition)
+ if self.records_per_buffer._get_byte() != 1:
+ logging.warning('records_per_buffer should be 1 in TS mode, '
+ 'defauling to 1')
+ self.records_per_buffer._set(1)
+ records_per_buffer = self.records_per_buffer._get_byte()
+
+ self._call_dll('AlazarBeforeAsyncRead',
+ self._handle, self.channel_selection,
+ self.transfer_offset, samples_per_buffer,
+ self.records_per_buffer, buffers_per_acquisition,
+ acquire_flags)
+
+ self.samples_per_record._set_updated()
+ self.records_per_buffer._set_updated()
+ self.buffers_per_acquisition._set_updated()
+ self.channel_selection._set_updated()
+ self.transfer_offset._set_updated()
+ self.external_startcapture._set_updated()
+ self.enable_record_headers._set_updated()
+ self.alloc_buffers._set_updated()
+ self.fifo_only_streaming._set_updated()
+ self.interleave_samples._set_updated()
+ self.get_processed_data._set_updated()
+
+ # create buffers for acquisition
+ self.clear_buffers()
+ # make sure that allocated_buffers <= buffers_per_acquisition
+ if (self.allocated_buffers._get_byte() >
+ self.buffers_per_acquisition._get_byte()):
+ print("'allocated_buffers' should be smaller than or equal to"
+ "'buffers_per_acquisition'. Defaulting 'allocated_buffers' to"
+ "" + str(self.buffers_per_acquisition._get_byte()))
+ self.allocated_buffers._set(
+ self.buffers_per_acquisition._get_byte())
+
+ allocated_buffers = self.allocated_buffers._get_byte()
+
+ for k in range(allocated_buffers):
+ try:
+ self.buffer_list.append(Buffer(bps, samples_per_buffer,
+ number_of_channels))
+ except:
+ self.clear_buffers()
+ raise
+
+ # post buffers to Alazar
+ for buf in self.buffer_list:
+ self._call_dll('AlazarPostAsyncBuffer',
+ self._handle, buf.addr, buf.size_bytes)
+ self.allocated_buffers._set_updated()
+
+ # -----start capture here-----
+ acquisition_controller.pre_start_capture()
+ # call the startcapture method
+ self._call_dll('AlazarStartCapture', self._handle)
+
+ acquisition_controller.pre_acquire()
+ # buffer handling from acquisition
+ buffers_completed = 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():
+ buf = self.buffer_list[buffers_completed % allocated_buffers]
+
+ self._call_dll('AlazarWaitAsyncBufferComplete',
+ self._handle, buf.addr, buffer_timeout)
+
+ # TODO(damazter) (C) last series of buffers must be handled
+ # exceptionally
+ # (and I want to test the difference) by changing buffer
+ # recycling for the last series of buffers
+
+ # 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
+
+ # 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(buf.buffer)
+
+ # free up memory
+ self.clear_buffers()
+
+ # check if all parameters are up to date
+ for p in self.parameters.values():
+ p.get()
+
+ # return result
+ return acquisition_controller.post_acquire()
+
+ def _set_if_present(self, param_name, value):
+ if value is not None:
+ self.parameters[param_name]._set(value)
+
+ def _set_list_if_present(self, param_base, value):
+ if value is not None:
+ for i, v in enumerate(value):
+ self.parameters[param_base + str(i + 1)]._set(v)
+
+ def _call_dll(self, func_name, *args):
+ """
+ Execute a dll function `func_name`, passing it the given arguments
+
+ For each argument in the list
+ - If an arg is a parameter of this instrument, the parameter
+ value from `._get_bytes()` is used. If the call succeeds, these
+ parameters will be marked as updated using their `._set_updated()`
+ method
+ - Otherwise the arg is used directly
+ """
+ # create the argument list
+ args_out = []
+ update_params = []
+ for arg in args:
+ if isinstance(arg,AlazarParameter):
+ args_out.append(arg._get_byte())
+ update_params.append(arg)
+ else:
+ args_out.append(arg)
+
+ # run the function
+ func = getattr(self._ATS_dll, func_name)
+ return_code = func(*args_out)
+
+ # check for errors
+ if (return_code != self._success) and (return_code !=518):
+ # TODO(damazter) (C) log error
+
+ argrepr = repr(args_out)
+ if len(argrepr) > 100:
+ argrepr = argrepr[:96] + '...]'
+
+ if return_code not in self._error_codes:
+ raise RuntimeError(
+ 'unknown error {} from function {} with args: {}'.format(
+ return_code, func_name, argrepr))
+ raise RuntimeError(
+ 'error {}: {} from function {} with args: {}'.format(
+ return_code, self._error_codes[return_code], func_name,
+ argrepr))
+
+ # mark parameters updated (only after we've checked for errors)
+ for param in update_params:
+ param._set_updated()
+
+ def clear_buffers(self):
+ for b in self.buffer_list:
+ b.free_mem()
+ self.buffer_list = []
+
+ def signal_to_volt(self, channel, signal):
+ # TODO(damazter) (S) check this
+ # TODO(damazter) (M) use byte value if range{channel}
+ return (((signal - 127.5) / 127.5) *
+ (self.parameters['channel_range' + str(channel)].get()))
+
+ def get_sample_rate(self):
+ if self.sample_rate.get() == 'EXTERNAL_CLOCK':
+ raise Exception('External clock is used, alazar driver '
+ 'could not determine sample speed.')
+
+ rate = self.sample_rate.get()
+ if rate == '1GHz_REFERENCE_CLOCK':
+ rate = 1e9
+
+ decimation = self.decimation.get()
+ if decimation > 0:
+ return rate / decimation
+ else:
+ return rate
+
+
+class AlazarParameter(Parameter):
+ def __init__(self, name=None, label=None, unit=None, instrument=None,
+ value=None, byte_to_value_dict=None, vals=None):
+ if vals is None:
+ if byte_to_value_dict is None:
+ vals = validators.Anything()
+ else:
+ # TODO(damazter) (S) test this validator
+ vals = validators.Enum(*byte_to_value_dict.values())
+
+ super().__init__(name=name, label=label, units=unit, vals=vals)
+ self.instrument = instrument
+ self._byte = None
+ self._uptodate_flag = False
+
+ # TODO(damazter) (M) check this block
+ if byte_to_value_dict is None:
+ self._byte_to_value_dict = TrivialDictionary()
+ self._value_to_byte_dict = TrivialDictionary()
+ else:
+ self._byte_to_value_dict = byte_to_value_dict
+ self._value_to_byte_dict = {
+ v: k for k, v in self._byte_to_value_dict.items()}
+
+ self._set(value)
+
+ def get(self):
+ """
+ This method returns the name of the value set for this parameter
+ :return: value
+ """
+ # TODO(damazter) (S) test this exception
+ if self._uptodate_flag is False:
+ raise Exception('The value of this parameter (' + self.name +
+ ') is not up to date with the actual value in '
+ 'the instrument.\n'
+ 'Most probable cause is illegal usage of ._set() '
+ 'method of this parameter.\n'
+ 'Don\'t use private methods if you do not know '
+ 'what you are doing!')
+ return self._byte_to_value_dict[self._byte]
+
+ def _get_byte(self):
+ """
+ this method gets the byte representation of the value of the parameter
+ :return: byte representation
+ """
+ return self._byte
+
+ def _set(self, value):
+ """
+ This method sets the value of this parameter
+ This method is private to ensure that all values in the instruments
+ are up to date always
+ :param value: the new value (e.g. 'NPT', 0.5, ...)
+ :return: None
+ """
+
+ # TODO(damazter) (S) test this validation
+ self.validate(value)
+ self._byte = self._value_to_byte_dict[value]
+ self._uptodate_flag = False
+ self._save_val(value)
+ return None
+
+ 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
+
+ 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
+ e = ctypes.windll.kernel32.GetLastError()
+ raise Exception("Memory allocation error: " + str(e))
+
+ 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):
+ 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
+
+ def __del__(self):
+ if self._allocated:
+ self.free_mem()
+ logging.warning(
+ 'Buffer prevented memory leak; Memory released to Windows.\n'
+ 'Memory should have been released before buffer was deleted.')
+
+
+class AcquisitionController(Instrument):
+ def __init__(self, name, alazar_name, **kwargs):
+ """
+ :param alazar_name: The name of the alazar instrument on the server
+ :return: nothing
+ """
+ super().__init__(name, **kwargs)
+ self.alazar = None
+ for inst in kwargs['server'].instruments.values():
+ if inst.name == alazar_name:
+ self._set_alazar(inst)
+ if self._get_alazar() is None:
+ raise LookupError("No instrument with the name " +
+ str(alazar_name) +
+ " was found on this instrument server")
+
+ def _get_alazar(self):
+ return self.alazar
+
+ def _set_alazar(self, alazar):
+ self.alazar = alazar
+
+ def pre_start_capture(self):
+ """
+ Use this method to prepare yourself for the data acquisition
+ The Alazar instrument will call this method right before
+ 'AlazarStartCapture' is called
+ :return:
+ """
+ raise NotImplementedError(
+ 'This method should be implemented in a subclass')
+
+ def pre_acquire(self):
+ """
+ This method is called immediately after 'AlazarStartCapture' is called
+ :return: nothing
+ """
+ raise NotImplementedError(
+ 'This method should be implemented in a subclass')
+
+ def handle_buffer(self, buffer):
+ """
+ This method should store or process the information that is contained
+ in the buffers obtained during the acquisition.
+ :param buffer: np.array with the data from the Alazar card
+ :return: something, it is ignored in any case
+ """
+ raise NotImplementedError(
+ 'This method should be implemented in a subclass')
+
+ def post_acquire(self):
+ """
+ This method should return any information you want to save from this
+ acquisition. The acquisition method from the Alazar driver will use
+ this data as its own return value
+ :return: this function should return all relevant data that you want
+ to get form the acquisition
+ """
+ raise NotImplementedError(
+ 'This method should be implemented in a subclass')
+
+
+class TrivialDictionary:
+ def __init__(self):
+ pass
+
+ def __getitem__(self, item):
+ return item
+
+ def __contains__(self, item):
+ # this makes sure that this dictionary contains everything
+ return True
diff --git a/qcodes/instrument_drivers/AlazarTech/ATS9870.py b/qcodes/instrument_drivers/AlazarTech/ATS9870.py
new file mode 100644
index 00000000000..32cd383de1e
--- /dev/null
+++ b/qcodes/instrument_drivers/AlazarTech/ATS9870.py
@@ -0,0 +1,284 @@
+from .ATS import AlazarTech_ATS, AlazarParameter
+from qcodes.utils import validators
+
+
+class AlazarTech_ATS9870(AlazarTech_ATS):
+ def __init__(self, name, **kwargs):
+ dll_path = 'C:\\WINDOWS\\System32\\ATSApi.dll'
+ super().__init__(name, dll_path=dll_path, **kwargs)
+ # 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=1000000000,
+ 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='AC',
+ 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=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='bwlimit' + i,
+ parameter_class=AlazarParameter,
+ label='Bandwidth limit channel ' + i,
+ unit=None,
+ value='DISABLED',
+ byte_to_value_dict={0: 'DISABLED',
+ 1: 'ENABLED'})
+
+ 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' if i == 0 else 'K'),
+ 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=128,
+ 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))
+ 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 of some number (64 in the
+ # case of ATS9870) and and has some minimum (256 in the case of ATS9870)
+ # These values can be found in the ATS-SDK programmar's guide
+ self.add_parameter(name='samples_per_record',
+ parameter_class=AlazarParameter,
+ label='Samples per Record',
+ unit=None,
+ value=96000,
+ vals=Multiples(divisor=64, min_value=256))
+
+ # TODO(damazter) (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=1,
+ vals=validators.Ints(min_value=0))
+ self.add_parameter(name='buffers_per_acquisition',
+ parameter_class=AlazarParameter,
+ label='Buffers per Acquisition',
+ unit=None,
+ value=1,
+ 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=1,
+ 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))
+
+ model = self.get_idn()['model']
+ if model != 'ATS9870':
+ raise Exception("The Alazar board kind is not 'ATS9870',"
+ " found '" + str(model) + "' instead.")
+
+
+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
new file mode 100644
index 00000000000..83527773c21
--- /dev/null
+++ b/qcodes/instrument_drivers/AlazarTech/ATS_acquisition_controllers.py
@@ -0,0 +1,96 @@
+from .ATS import AcquisitionController
+import math
+import numpy as np
+
+
+# DFT AcquisitionController
+class DFT_AcquisitionController(AcquisitionController):
+ def __init__(self, name, alazar_name, demodulation_frequency, **kwargs):
+ self.demodulation_frequency = demodulation_frequency
+ self.acquisitionkwargs = {'acquisition_controller': self}
+ self.samples_per_record = None
+ self.records_per_buffer = None
+ self.buffers_per_acquisition = None
+ # TODO(damazter) (S) this is not very general:
+ self.number_of_channels = 2
+ self.cos_list = None
+ self.sin_list = None
+ self.buffer = None
+ # make a call to the parent class and by extension, create the parameter
+ # structure of this class
+ super().__init__(name, alazar_name, **kwargs)
+ self.add_parameter("acquisition", get_cmd=self.do_acquisition)
+
+ def set_acquisitionkwargs(self, **kwargs):
+ self.acquisitionkwargs.update(**kwargs)
+
+ def do_acquisition(self):
+ value = self._get_alazar().acquire(**self.acquisitionkwargs)
+ return value
+
+ def pre_start_capture(self):
+ alazar = self._get_alazar()
+ 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()
+ sample_speed = alazar.get_sample_rate()
+ integer_list = np.arange(self.samples_per_record)
+ angle_list = (2 * np.pi * self.demodulation_frequency / sample_speed *
+ integer_list)
+
+ self.cos_list = np.cos(angle_list)
+ self.sin_list = np.sin(angle_list)
+ self.buffer = np.zeros(self.samples_per_record *
+ self.records_per_buffer *
+ self.number_of_channels)
+
+ def pre_acquire(self):
+ # this could be used to start an Arbitrary Waveform Generator, etc...
+ # using this method ensures that the contents are executed AFTER the
+ # Alazar card starts listening for a trigger pulse
+ pass
+
+ def handle_buffer(self, data):
+ self.buffer += data
+
+ def post_acquire(self):
+ alazar = self._get_alazar()
+ # average all records in a buffer
+ 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
+ i1 = i0 + self.samples_per_record
+ recordA += self.buffer[i0:i1] / records_per_acquisition
+
+ recordB = np.zeros(self.samples_per_record)
+ for i in range(self.records_per_buffer):
+ i0 = i * self.samples_per_record + len(self.buffer) / 2
+ i1 = i0 + self.samples_per_record
+ recordB += self.buffer[i0:i1] / records_per_acquisition
+
+ if self.number_of_channels == 2:
+ # fit channel A and channel B
+ res1 = self.fit(recordA)
+ res2 = self.fit(recordB)
+ #return [alazar.signal_to_volt(1, res1[0] + 127.5),
+ # alazar.signal_to_volt(2, res2[0] + 127.5),
+ # res1[1], res2[1],
+ # (res1[1] - res2[1]) % 360]
+ return alazar.signal_to_volt(1, res1[0] + 127.5)
+ else:
+ raise Exception("Could not find CHANNEL_B during data extraction")
+ return None
+
+ def fit(self, buf):
+ # Discrete Fourier Transform
+ RePart = np.dot(buf - 127.5, self.cos_list) / self.samples_per_record
+ ImPart = np.dot(buf - 127.5, self.sin_list) / self.samples_per_record
+
+ # the factor of 2 in the amplitude is due to the fact that there is
+ # a negative frequency as well
+ ampl = 2 * np.sqrt(RePart ** 2 + ImPart ** 2)
+
+ # see manual page 52!!! (using unsigned data)
+ return [ampl, math.atan2(ImPart, RePart) * 360 / (2 * math.pi)]
diff --git a/qcodes/instrument_drivers/AlazarTech/__init__.py b/qcodes/instrument_drivers/AlazarTech/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d