diff --git a/docs/examples/Qcodes example with ZI UHF-LI.ipynb b/docs/examples/Qcodes example with ZI UHF-LI.ipynb
new file mode 100644
index 00000000000..e90b3d1fb77
--- /dev/null
+++ b/docs/examples/Qcodes example with ZI UHF-LI.ipynb
@@ -0,0 +1,3144 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# QCoDeS Example with ZI UHF-LI\n",
+ "\n",
+ "\n",
+ "## Table of Contents\n",
+ "\n",
+ "* [Prerequisites](#prereq)\n",
+ "* [Basic Usage of the ZI UHF-LI](#basic)\n",
+ " * [Signal Inputs](#sigins)\n",
+ " * [Demodulators](#demods)\n",
+ " * [Signal Outputs](#sigouts)\n",
+ "* [Using the Sweeper](#sweeper)\n",
+ "* [Using the Scope](#scope)\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {
+ "collapsed": false,
+ "deletable": true,
+ "editable": true,
+ "scrolled": true
+ },
+ "outputs": [],
+ "source": [
+ "%matplotlib notebook\n",
+ "import time\n",
+ "import logging\n",
+ "import numpy as np\n",
+ "import matplotlib.pyplot as plt\n",
+ "plt.ion()\n",
+ "\n",
+ "import qcodes as qc\n",
+ "from qcodes.instrument_drivers.ZI.ZIUHFLI import ZIUHFLI\n",
+ "\n",
+ "log = logging.getLogger(__name__)\n",
+ "log.setLevel(logging.INFO)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "deletable": true,
+ "editable": true
+ },
+ "source": [
+ "# Prerequisites \n",
+ "\n",
+ "It is necessary to download and install the ZI Lab One software. Additionally, both the data server and the web server must run, and a connection to the instrument must be instantiated (this can be done via the web interface). This example notebook makes no assumptions on what is connected to the instrument (but if you want nice data plots, go ahead and connect something interesting)."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 24,
+ "metadata": {
+ "collapsed": false,
+ "deletable": true,
+ "editable": true,
+ "scrolled": false
+ },
+ "outputs": [],
+ "source": [
+ "# Instantiate the QCoDeS instrument\n",
+ "zi = ZIUHFLI('ZIUHFLI', 'dev2235')\n",
+ "\n",
+ "# Bind it to a QCoDeS station for later use (one should ALWAYS do this with instruments)\n",
+ "station = qc.Station(zi)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "deletable": true,
+ "editable": true
+ },
+ "source": [
+ "# Basic Usage of the ZI UHF-LI \n",
+ "\n",
+ "Most of the \"front panel\" (i.e. the Web UI) `Lock-In` and `signal input` settings are available as parameters."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 21,
+ "metadata": {
+ "collapsed": false,
+ "deletable": true,
+ "editable": true
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Oscillator 2 has frequency: 752100 Hz\n"
+ ]
+ }
+ ],
+ "source": [
+ "zi.oscillator2_freq.set(752.1e3)\n",
+ "print('Oscillator 2 has frequency: {:.0f} Hz'.format(zi.oscillator2_freq.get()))\n",
+ "zi.signal_input1_range(1)\n",
+ "zi.signal_input1_scaling(1)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Signal Inputs "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 22,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Available signal input settings:\n",
+ "\n",
+ " signal_input1_AC, AC coupling ()\n",
+ " signal_input1_impedance, Input impedance ()\n",
+ " signal_input1_scaling, Input scaling ()\n",
+ " signal_input1_diff, Input signal subtraction ()\n",
+ " signal_input1_range, Input range (V)\n"
+ ]
+ }
+ ],
+ "source": [
+ "print('Available signal input settings:\\n')\n",
+ "for param in [p for p in zi.parameters if 'signal_input1' in p]:\n",
+ " print(' {}, {} ({})'.format(param, zi.parameters[param].label, zi.parameters[param].unit))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "deletable": true,
+ "editable": true
+ },
+ "source": [
+ "## Demodulators "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {
+ "collapsed": false,
+ "deletable": true,
+ "editable": true
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Available demodulator settings:\n",
+ "\n",
+ " demod1_streaming, Data streaming ()\n",
+ " demod1_timeconstant, Filter time constant (s)\n",
+ " demod1_samplerate, Sample rate (Sa/s)\n",
+ " demod1_phaseshift, Phase shift (degrees)\n",
+ " demod1_sinc, Sinc filter ()\n",
+ " demod1_harmonic, Reference frequency multiplication factor ()\n",
+ " demod1_signalin, Signal input ()\n",
+ " demod1_order, Filter order ()\n",
+ " demod1_trigger, Trigger ()\n"
+ ]
+ }
+ ],
+ "source": [
+ "print('Available demodulator settings:\\n')\n",
+ "for param in [p for p in zi.parameters if 'demod1' in p]:\n",
+ " print(' {}, {} ({})'.format(param, zi.parameters[param].label, zi.parameters[param].unit))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Signal Outputs "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Available signal output settings:\n",
+ "\n",
+ " signal_output1_enable, Enable signal output's amplitude. ()\n",
+ " signal_output1_offset, Signal output offset (V)\n",
+ " signal_output1_imp50, Switch to turn on 50 Ohm impedance ()\n",
+ " signal_output1_on, Turn signal output on and off. ()\n",
+ " signal_output1_range, Signal output range ()\n",
+ " signal_output1_autorange, Enable signal output range. ()\n",
+ " signal_output1_ampdef, Signal output amplitude's definition (V)\n",
+ " signal_output1_amplitude, Signal output amplitude (V)\n"
+ ]
+ }
+ ],
+ "source": [
+ "print('Available signal output settings:\\n')\n",
+ "for param in [p for p in zi.parameters if 'signal_output1' in p]:\n",
+ " print(' {}, {} ({})'.format(param, zi.parameters[param].label, zi.parameters[param].unit))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "deletable": true,
+ "editable": true
+ },
+ "source": [
+ "# Using the sweeper \n",
+ "\n",
+ "The QCoDeS sweep is fully indendent of the GUI Sweep, meaning that parameters set in QCoDeS will not affect the GUI sweep and vice versa.\n",
+ "\n",
+ "The sweeper settings are configured via a bunch of parameters, all named `sweeper_XXX`.\n",
+ "This configures the x-axis of the sweep as well as the sweep acquisition settings. To learn more about what a certain parameter does, it is sometimes helpful to print its `__doc__` attribute.\n",
+ "\n",
+ "Which signals are **returned** by the sweeper is controlled by adding (removing) signals to (from) the sweep.\n",
+ "\n",
+ "The sweep settings can be displayed with the `print_sweeper_settings` command.\n",
+ "\n",
+ "Before the sweep can be performed, it must be built. \n",
+ "This is done with the Sweep parameter, which is the parameter holding the sweep data.\n",
+ "Note that building the sweep may change some of the time constants, and in particular change the sweep time. In case of doubt, re-run `print_sweeper_settings`."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {
+ "collapsed": false,
+ "deletable": true,
+ "editable": true
+ },
+ "outputs": [],
+ "source": [
+ "# Set up a sweep sweeping an internal oscillator frequency from 1 MHz to 5 MHz\n",
+ "# We want the sweeper to sweep linearly over 200 points\n",
+ "zi.sweeper_param('Osc 1 Frequency')\n",
+ "zi.sweeper_xmapping('lin')\n",
+ "zi.sweeper_start(1e6)\n",
+ "zi.sweeper_stop(10e6)\n",
+ "zi.sweeper_samplecount(100)\n",
+ "zi.sweeper_BWmode('fixed')\n",
+ "zi.sweeper_BW(250)\n",
+ "zi.sweeper_order(4)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "metadata": {
+ "collapsed": false,
+ "deletable": true,
+ "editable": true
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "\n",
+ " For each sweep point, the demodulator\n",
+ " filter bandwidth (time constant) may\n",
+ " be either set automatically, be the\n",
+ " current demodulator bandwidth or be\n",
+ " a fixed number; the sweeper_BW\n",
+ " parameter.\n",
+ " \r\n",
+ "\r\n",
+ "Parameter class:\r\n",
+ "\r\n",
+ "* `name` sweeper_BWmode\r\n",
+ "* `label` Sweeper bandwidth control mode\r\n",
+ "* `unit` \r\n",
+ "* `vals` \n"
+ ]
+ }
+ ],
+ "source": [
+ "# I wonder what the sweeper BWmode does...\n",
+ "print(zi.sweeper_BWmode.__doc__)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "metadata": {
+ "collapsed": true,
+ "deletable": true,
+ "editable": true
+ },
+ "outputs": [],
+ "source": [
+ "# Add three signals to the sweep, all measured on demodulator 1\n",
+ "zi.add_signal_to_sweeper(1, 'Xrms')\n",
+ "zi.add_signal_to_sweeper(1, 'Yrms')\n",
+ "zi.add_signal_to_sweeper(1, 'Rrms')"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "metadata": {
+ "collapsed": false,
+ "deletable": true,
+ "editable": true
+ },
+ "outputs": [],
+ "source": [
+ "# Make sure that demodulator 1 is measuring what and as it should\n",
+ "zi.demod1_trigger('Continuous')\n",
+ "zi.demod1_signalin('Sig In 1')"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 11,
+ "metadata": {
+ "collapsed": false,
+ "deletable": true,
+ "editable": true
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "ACQUISITION\n",
+ " Sweeper bandwidth control mode: fixed ()\n",
+ " Fixed bandwidth sweeper bandwidth (NEP): 50.0 ()\n",
+ " Sweeper filter order: 1 ()\n",
+ " Minimal no. of samples to average at each sweep point: 25 ()\n",
+ " Minimal averaging time: 0.1 (s)\n",
+ " Minimal settling time for the sweeper: 1e-06 (s)\n",
+ " Sweep filter settling time: 4.605170185988091 (dim. less.)\n",
+ "HORISONTAL\n",
+ " Start value of the sweep: 1000000.0\n",
+ " Stop value of the sweep: 10000000.0\n",
+ " Units of sweep x-axis: Hz\n",
+ " Length of the sweep (pts): 25\n",
+ " Parameter to sweep (sweep x-axis): Osc 1 Frequency\n",
+ " Sweep mode: Sequential\n",
+ " Sweep timeout: 600\n",
+ "VERTICAL\n",
+ " Signal 1: Demodulator 1: Xrms\n",
+ " Signal 2: Demodulator 1: Yrms\n",
+ " Signal 3: Demodulator 1: Rrms\n",
+ "DEMODULATORS\n",
+ " Demodulator 1: Filter time constant: 0.000311 (s)\n",
+ " Demodulator 1: Filter order: 4.000000 ()\n",
+ " Demodulator 1: Sample rate: 1716.613770 (Sa/s)\n",
+ "META\n",
+ " Expected sweep time: 0.9 (s)\n",
+ " Sweep timeout: 600 (s)\n",
+ " Sweep built and ready to execute: False\n"
+ ]
+ }
+ ],
+ "source": [
+ "# I wonder what kind of sweep we have made now...\n",
+ "zi.print_sweeper_settings()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 12,
+ "metadata": {
+ "collapsed": false,
+ "deletable": true,
+ "editable": true
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "ACQUISITION\n",
+ " Sweeper bandwidth control mode: fixed ()\n",
+ " Fixed bandwidth sweeper bandwidth (NEP): 250.0 ()\n",
+ " Sweeper filter order: 4 ()\n",
+ " Minimal no. of samples to average at each sweep point: 25 ()\n",
+ " Minimal averaging time: 0.1 (s)\n",
+ " Minimal settling time for the sweeper: 1e-06 (s)\n",
+ " Sweep filter settling time: 9.998049677807453 (dim. less.)\n",
+ "HORISONTAL\n",
+ " Start value of the sweep: 1000000.0\n",
+ " Stop value of the sweep: 10000000.0\n",
+ " Units of sweep x-axis: Hz\n",
+ " Length of the sweep (pts): 100\n",
+ " Parameter to sweep (sweep x-axis): Osc 1 Frequency\n",
+ " Sweep mode: Sequential\n",
+ " Sweep timeout: 600\n",
+ "VERTICAL\n",
+ " Signal 1: Demodulator 1: Xrms\n",
+ " Signal 2: Demodulator 1: Yrms\n",
+ " Signal 3: Demodulator 1: Rrms\n",
+ "DEMODULATORS\n",
+ " Demodulator 1: Filter time constant: 0.000311 (s)\n",
+ " Demodulator 1: Filter order: 4.000000 ()\n",
+ " Demodulator 1: Sample rate: 1716.613770 (Sa/s)\n",
+ "META\n",
+ " Expected sweep time: 1.8 (s)\n",
+ " Sweep timeout: 600 (s)\n",
+ " Sweep built and ready to execute: True\n"
+ ]
+ }
+ ],
+ "source": [
+ "# Gee, that looks good! Note the last line, the sweep is NOT ready to execute.\n",
+ "zi.Sweep.build_sweep()\n",
+ "# Now it is!\n",
+ "zi.print_sweeper_settings()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 13,
+ "metadata": {
+ "collapsed": false,
+ "deletable": true,
+ "editable": true
+ },
+ "outputs": [],
+ "source": [
+ "# We can now execute the sweeper by simply invoking Sweep.get\n",
+ "# This returns a tuple with the signals we asked for\n",
+ "(X, Y, R) = zi.Sweep.get()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 14,
+ "metadata": {
+ "collapsed": false,
+ "deletable": true,
+ "editable": true
+ },
+ "outputs": [
+ {
+ "data": {
+ "application/javascript": [
+ "/* Put everything inside the global mpl namespace */\n",
+ "window.mpl = {};\n",
+ "\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",
+ " if (mpl.ratio != 1) {\n",
+ " fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n",
+ " }\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 backingStore = this.context.backingStorePixelRatio ||\n",
+ "\tthis.context.webkitBackingStorePixelRatio ||\n",
+ "\tthis.context.mozBackingStorePixelRatio ||\n",
+ "\tthis.context.msBackingStorePixelRatio ||\n",
+ "\tthis.context.oBackingStorePixelRatio ||\n",
+ "\tthis.context.backingStorePixelRatio || 1;\n",
+ "\n",
+ " mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\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 * mpl.ratio);\n",
+ " canvas.attr('height', height * mpl.ratio);\n",
+ " canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\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'] / mpl.ratio;\n",
+ " var y0 = (fig.canvas.height - msg['y0']) / mpl.ratio;\n",
+ " var x1 = msg['x1'] / mpl.ratio;\n",
+ " var y1 = (fig.canvas.height - msg['y1']) / mpl.ratio;\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 * mpl.ratio;\n",
+ " var y = canvas_pos.y * mpl.ratio;\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\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\"];\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",
+ " var width = fig.canvas.width/mpl.ratio\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 width = this.canvas.width/mpl.ratio\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",
+ " // select the cell after this one\n",
+ " var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n",
+ " IPython.notebook.select(index + 1);\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": 14,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "# This we may manually plot.\n",
+ "plt.figure()\n",
+ "plt.yscale('log')\n",
+ "plt.plot(X, lw=2, color=(0.5, 0, 0.3))\n",
+ "plt.plot(Y, lw=2, color=(0.5, 0.3, 0))\n",
+ "plt.plot(R, lw=2, color=(0.3, 0, 0.5))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 15,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Debug (100,) (100,)\n",
+ "Debug (100,) (100,)\n",
+ "Debug (100,) (100,)\n",
+ "DataSet:\n",
+ " mode = DataMode.LOCAL\n",
+ " location = 'data/2017-03-01/#011_{name}_11-49-47'\n",
+ " | | | \n",
+ " Measured | Hz | Hz | (100,)\n",
+ " Measured | ZIUHFLI_Xrms | Xrms | (100,)\n",
+ " Measured | ZIUHFLI_Yrms | Yrms | (100,)\n",
+ " Measured | ZIUHFLI_Rrms | Rrms | (100,)\n",
+ "acquired at 2017-03-01 11:49:51\n"
+ ]
+ }
+ ],
+ "source": [
+ "# A more correct way to do this is to perform a proper QCoDeS measurement\n",
+ "sweepdata = qc.Measure(zi.Sweep).run()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "deletable": true,
+ "editable": true
+ },
+ "source": [
+ "# Using the scope \n",
+ "\n",
+ "The scope is in a strange half-finished state, which is due to Zurich Instruments not yet shipping the full software package for the scope.\n",
+ "\n",
+ "The QCoDeS scope is mostly synced with the GUI scope, with the averaging as a notable exeption. Please set ALL values using QCoDeS before performing a measurement."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## A word of caution\n",
+ "\n",
+ "Many of the scope parameters depend on each other. We exemplify this now."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 16,
+ "metadata": {
+ "collapsed": false,
+ "deletable": true,
+ "editable": true
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Current scope settings: SR: 56.2 MHz, Length: 7030, duration: 0.000125 (s)\n",
+ "The length is 562000, 562000\n",
+ "The duration is 0.005, 0.005 and the length is 281000\n",
+ "Finally, length: 281000 points, duration : 0.01 (s)\n"
+ ]
+ }
+ ],
+ "source": [
+ "# Sampling rate, trace length, and trace duration all depend on each other\n",
+ "zi.scope_samplingrate('56.2 MHz') # starting value for example\n",
+ "\n",
+ "(SR, length, dur) = (zi.scope_samplingrate(), zi.scope_length(), zi.scope_duration())\n",
+ "print('Current scope settings: SR: {}, Length: {:d}, duration: {:.6f} ({})'.format(SR, int(length), dur, zi.scope_duration.unit))\n",
+ "\n",
+ "# Now we change the \n",
+ "zi.scope_duration(10e-3)\n",
+ "# The driver has now updated the length behind the scenes, so that the internal QCoDeS length matches what a new\n",
+ "# query will return\n",
+ "print('The length is {}, {}'.format(zi.scope_length.get_latest(), zi.scope_length.get()))\n",
+ "\n",
+ "# Similarly if we change the number of points to half of the current value\n",
+ "oldN = zi.scope_length.get_latest()\n",
+ "zi.scope_length(int(oldN/2))\n",
+ "print('The duration is {}, {} and the length is {}'.format(zi.scope_duration.get_latest(),\n",
+ " zi.scope_duration.get(),\n",
+ " zi.scope_length.get()))\n",
+ "\n",
+ "# Finally, note that changing the sampling rate does not change the length, but the duration\n",
+ "zi.scope_samplingrate('28.1 MHz')\n",
+ "print('Finally, length: {} points, duration : {} (s)'.format(zi.scope_length.get_latest(), zi.scope_duration.get_latest()))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "collapsed": false,
+ "deletable": true,
+ "editable": true
+ },
+ "source": [
+ "## Setting up a scope measurement"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 16,
+ "metadata": {
+ "collapsed": false,
+ "deletable": true,
+ "editable": true
+ },
+ "outputs": [],
+ "source": [
+ "# You must define at least everything. Here we go through the GUI settings one-by-one.\n",
+ "\n",
+ "# THE CONTROL TAB\n",
+ "# Horisontal\n",
+ "zi.scope_mode.set('Time Domain')\n",
+ "zi.scope_samplingrate.set('7.03 MHz')\n",
+ "zi.scope_duration.set(1e-3) # seconds\n",
+ "# Vertical\n",
+ "zi.scope_channel1_input.set('Signal Input 1')\n",
+ "zi.scope_channel2_input.set('Signal Input 2')\n",
+ "zi.scope_channels.set(3) # 1: Chan1 only, 2: Chan2 only, 3: Chan1 + Chan2\n",
+ "zi.scope_average_weight(3) # Number of averages\n",
+ "\n",
+ "# THE TRIG TAB\n",
+ "# Trigger\n",
+ "zi.scope_trig_enable.set('ON')\n",
+ "zi.scope_trig_signal.set('Signal Input 2')\n",
+ "zi.scope_trig_slope.set('Rise')\n",
+ "zi.scope_trig_level.set(20e-3) # Volts if the input is volts\n",
+ "zi.scope_trig_hystmode.set('absolute')\n",
+ "zi.scope_trig_hystabsolute.set(3e-3) # Volts if the input is volts\n",
+ "zi.scope_trig_gating_enable.set('OFF')\n",
+ "zi.scope_trig_gating_source.set('Trigger In 4 Low')\n",
+ "zi.scope_trig_holdoffmode.set('s') # QCoDeS currently does not support a holdoff in events. Ask William why. \n",
+ "zi.scope_trig_holdoffseconds.set(2e-5)\n",
+ "zi.scope_trig_reference.set(0) # Sets the reference for the delay in percent of the trace duration, i.e. 0 is at the trigger\n",
+ "zi.scope_trig_delay.set(1e-4) # Sets the delay for the acquisition\n",
+ "# Segments (NB: Requires purchase of the DIG upgrade)\n",
+ "zi.scope_segments.set('ON')\n",
+ "zi.scope_segments_count.set(5)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 17,
+ "metadata": {
+ "collapsed": false,
+ "deletable": true,
+ "editable": true
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Is the scope ready? -False\n",
+ "How about now? Can the scope be run? -True\n"
+ ]
+ }
+ ],
+ "source": [
+ "# Just like the sweeper, the scope must be prepared for a measurement.\n",
+ "print('Is the scope ready? -{}'.format(zi.scope_correctly_built))\n",
+ "\n",
+ "# Then the scope must be prepared for the measurement\n",
+ "zi.Scope.prepare_scope()\n",
+ "\n",
+ "#\n",
+ "print('How about now? Can the scope be run? -{}'.format(zi.scope_correctly_built))\n",
+ "\n",
+ "# And data can be pulled out with the get command\n",
+ "data = zi.Scope.get()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 18,
+ "metadata": {
+ "collapsed": false,
+ "deletable": true,
+ "editable": true
+ },
+ "outputs": [
+ {
+ "data": {
+ "application/javascript": [
+ "/* Put everything inside the global mpl namespace */\n",
+ "window.mpl = {};\n",
+ "\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",
+ " if (mpl.ratio != 1) {\n",
+ " fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n",
+ " }\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 backingStore = this.context.backingStorePixelRatio ||\n",
+ "\tthis.context.webkitBackingStorePixelRatio ||\n",
+ "\tthis.context.mozBackingStorePixelRatio ||\n",
+ "\tthis.context.msBackingStorePixelRatio ||\n",
+ "\tthis.context.oBackingStorePixelRatio ||\n",
+ "\tthis.context.backingStorePixelRatio || 1;\n",
+ "\n",
+ " mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\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 * mpl.ratio);\n",
+ " canvas.attr('height', height * mpl.ratio);\n",
+ " canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\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'] / mpl.ratio;\n",
+ " var y0 = (fig.canvas.height - msg['y0']) / mpl.ratio;\n",
+ " var x1 = msg['x1'] / mpl.ratio;\n",
+ " var y1 = (fig.canvas.height - msg['y1']) / mpl.ratio;\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 * mpl.ratio;\n",
+ " var y = canvas_pos.y * mpl.ratio;\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\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\"];\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",
+ " var width = fig.canvas.width/mpl.ratio\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 width = this.canvas.width/mpl.ratio\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",
+ " // select the cell after this one\n",
+ " var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n",
+ " IPython.notebook.select(index + 1);\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": {
+ "application/javascript": [
+ "/* Put everything inside the global mpl namespace */\n",
+ "window.mpl = {};\n",
+ "\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",
+ " if (mpl.ratio != 1) {\n",
+ " fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n",
+ " }\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 backingStore = this.context.backingStorePixelRatio ||\n",
+ "\tthis.context.webkitBackingStorePixelRatio ||\n",
+ "\tthis.context.mozBackingStorePixelRatio ||\n",
+ "\tthis.context.msBackingStorePixelRatio ||\n",
+ "\tthis.context.oBackingStorePixelRatio ||\n",
+ "\tthis.context.backingStorePixelRatio || 1;\n",
+ "\n",
+ " mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\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 * mpl.ratio);\n",
+ " canvas.attr('height', height * mpl.ratio);\n",
+ " canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\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'] / mpl.ratio;\n",
+ " var y0 = (fig.canvas.height - msg['y0']) / mpl.ratio;\n",
+ " var x1 = msg['x1'] / mpl.ratio;\n",
+ " var y1 = (fig.canvas.height - msg['y1']) / mpl.ratio;\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 * mpl.ratio;\n",
+ " var y = canvas_pos.y * mpl.ratio;\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\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\"];\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",
+ " var width = fig.canvas.width/mpl.ratio\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 width = this.canvas.width/mpl.ratio\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",
+ " // select the cell after this one\n",
+ " var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n",
+ " IPython.notebook.select(index + 1);\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"
+ }
+ ],
+ "source": [
+ "# And here is a small plotter\n",
+ "\n",
+ "for chandata in data:\n",
+ " if chandata is not None:\n",
+ " fig, axs = plt.subplots(len(chandata), 1)\n",
+ " for ind, ax in enumerate(axs):\n",
+ " ax.plot(chandata[ind, :])"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 19,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Debug (5,) (5,)\n",
+ "Debug (5, 7030) (5, 7030)\n",
+ "Debug (5,) (5,)\n",
+ "Debug (5, 7030) (5, 7030)\n",
+ "DataSet:\n",
+ " mode = DataMode.LOCAL\n",
+ " location = 'data/2017-03-01/#012_{name}_11-57-04'\n",
+ " | | | \n",
+ " Measured | Time | Time | (5, 7030)\n",
+ " Measured | ZIUHFLI_Sig. In 1 | Sig. In 1 | (5, 7030)\n",
+ " Measured | ZIUHFLI_Sig. In 2 | Sig. In 2 | (5, 7030)\n",
+ "acquired at 2017-03-01 11:57:05\n"
+ ]
+ }
+ ],
+ "source": [
+ "# But it is strongly encouraged to use measure with QCoDeS rather than by using the .get method directly.\n",
+ "\n",
+ "scopedata = qc.Measure(zi.Scope).run()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 12,
+ "metadata": {
+ "collapsed": false,
+ "deletable": true,
+ "editable": true
+ },
+ "outputs": [
+ {
+ "ename": "RuntimeError",
+ "evalue": "ZIAPINotFoundException with status code: 16387. Value or Node not found",
+ "output_type": "error",
+ "traceback": [
+ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
+ "\u001b[0;31mRuntimeError\u001b[0m Traceback (most recent call last)",
+ "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mzi\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mdaq\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mgetDouble\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'/dev2235/demods/0/value'\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
+ "\u001b[0;31mRuntimeError\u001b[0m: ZIAPINotFoundException with status code: 16387. Value or Node not found"
+ ]
+ }
+ ],
+ "source": [
+ "zi.daq.getDouble('/dev2235/demods/0/value')"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 23,
+ "metadata": {
+ "collapsed": true
+ },
+ "outputs": [],
+ "source": [
+ "zi.close()"
+ ]
+ }
+ ],
+ "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.2"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 1
+}
diff --git a/qcodes/instrument_drivers/ZI/ZIUHFLI.py b/qcodes/instrument_drivers/ZI/ZIUHFLI.py
new file mode 100644
index 00000000000..1f78667d953
--- /dev/null
+++ b/qcodes/instrument_drivers/ZI/ZIUHFLI.py
@@ -0,0 +1,1930 @@
+import time
+import logging
+import numpy as np
+from functools import partial
+
+try:
+ import zhinst.utils
+except ImportError:
+ raise ImportError('''Could not find Zurich Instruments Lab One software.
+ Please refer to the Zi UHF-LI User Manual for
+ download and installation instructions.
+ ''')
+
+from qcodes.instrument.parameter import ManualParameter
+from qcodes.instrument.parameter import MultiParameter
+from qcodes.instrument.base import Instrument
+from qcodes.utils import validators as vals
+
+log = logging.getLogger(__name__)
+
+
+class Sweep(MultiParameter):
+ """
+ Parameter class for the ZIUHFLI instrument class for the sweeper.
+
+ The get method returns a tuple of arrays, where each array contains the
+ values of a signal added to the sweep (e.g. demodulator 4 phase).
+
+ Attributes:
+ names (tuple): Tuple of strings containing the names of the sweep
+ signals (to be measured)
+ units (tuple): Tuple of strings containg the units of the signals
+ shapes (tuple): Tuple of tuples each containing the Length of a
+ signal.
+ setpoints (tuple): Tuple of N copies of the sweep x-axis points,
+ where N is he number of measured signals
+ setpoint_names (tuple): Tuple of N identical strings with the name
+ of the sweep x-axis.
+
+ """
+ def __init__(self, name, instrument, **kwargs):
+ # The __init__ requires that we supply names and shapes,
+ # but there is no way to know what they could be known at this time.
+ # They are updated via build_sweep.
+ super().__init__(name, names=('',), shapes=((1,),), **kwargs)
+ self._instrument = instrument
+
+ def build_sweep(self):
+ """
+ Build a sweep with the current sweep settings. Must be called
+ before the sweep can be executed.
+
+ For developers:
+ This is a general function for updating the sweeper.
+ Every time a parameter of the sweeper is changed, this function
+ must be called to update the sweeper. Although such behaviour is only
+ strictly necessary for parameters that affect the setpoints of the
+ Sweep parameter, having to call this function for any parameter is
+ deemed more user friendly (easier to remember; when? -always).
+
+ The function sets all (user specified) settings on the sweeper and
+ additionally sets names, units, and setpoints for the Sweep
+ parameter.
+
+ """
+
+ signals = self._instrument._sweeper_signals
+ sweepdict = self._instrument._sweepdict
+
+ log.info('Built a sweep')
+
+ sigunits = {'X': 'V', 'Y': 'V', 'R': 'Vrms', 'Xrms': 'Vrms',
+ 'Yrms': 'Vrms', 'Rrms': 'Vrms', 'phase': 'degrees'}
+ names = []
+ units = []
+ for sig in signals:
+ name = sig.split('/')[-1]
+ names.append(name)
+ units.append(sigunits[name])
+ self.names = tuple(names)
+ self.units = tuple(units)
+ self.labels = tuple(names) # TODO: What are good labels?
+
+ # TODO: what are good set point names?
+ spnamedict = {'auxouts/0/offset': 'Volts',
+ 'auxouts/1/offset': 'Volts',
+ 'auxouts/2/offset': 'Volts',
+ 'auxouts/3/offset': 'Volts',
+ 'demods/0/phaseshift': 'degrees',
+ 'demods/1/phaseshift': 'degrees',
+ 'demods/2/phaseshift': 'degrees',
+ 'demods/3/phaseshift': 'degrees',
+ 'demods/4/phaseshift': 'degrees',
+ 'demods/5/phaseshift': 'degrees',
+ 'demods/6/phaseshift': 'degrees',
+ 'demods/7/phaseshift': 'degrees',
+ 'oscs/0/freq': 'Hz',
+ 'oscs/1/freq': 'Hz',
+ 'sigouts/0/amplitudes/3': 'Volts',
+ 'sigouts/0/offset': 'Volts',
+ 'sigouts/1/amplitudes/7': 'Volts',
+ 'sigouts/1/offset': 'Volts'
+ }
+ sp_name = spnamedict[sweepdict['gridnode']]
+
+ self.setpoint_names = ((sp_name,),)*len(signals)
+ start = sweepdict['start']
+ stop = sweepdict['stop']
+ npts = sweepdict['samplecount']
+ # TODO: make sure that these setpoints are correct, i.e. actually
+ # matching what the UHFLI does
+ # TODO: support non-sequential sweep mode
+ if not sweepdict['scan'] == 0:
+ raise NotImplementedError('Only sequential scanning is supported.')
+ if sweepdict['xmapping'] == 'lin':
+ sw = tuple(np.linspace(start, stop, npts))
+ else:
+ logstart = np.log10(start)
+ logstop = np.log10(stop)
+ sw = tuple(np.logspace(logstart, logstop, npts))
+ self.setpoints = ((sw,),)*len(signals)
+ self.shapes = ((npts,),)*len(signals)
+
+ # Now actually send the settings to the instrument
+ for (setting, value) in sweepdict.items():
+ setting = 'sweep/' + setting
+ self._instrument.sweeper.set(setting, value)
+
+ self._instrument.sweep_correctly_built = True
+
+ def get(self):
+ """
+ Execute the sweeper and return the data corresponding to the
+ subscribed signals.
+
+ Returns:
+
+ tuple: Tuple containg N numpy arrays where N is the number
+ of signals added to the sweep.
+
+ Raises:
+ ValueError: If no signals have been added to the sweep
+ ValueError: If a sweep setting has been modified since
+ the last sweep, but Sweep.build_sweep has not been run
+ """
+ daq = self._instrument.daq
+ signals = self._instrument._sweeper_signals
+ sweeper = self._instrument.sweeper
+
+ if signals == []:
+ raise ValueError('No signals selected! Can not perform sweep.')
+
+ if self._instrument.sweep_correctly_built is False:
+ raise ValueError('The sweep has not been correctly built.' +
+ ' Please run Sweep.build_sweep.')
+
+ # We must enable the demodulators we use.
+ # After the sweep, they should be returned to their original state
+ streamsettings = [] # This list keeps track of the pre-sweep settings
+ for sigstr in signals:
+ path = '/'.join(sigstr.split('/')[:-1])
+ (_, dev, _, dmnum, _) = path.split('/')
+
+ # If the setting has never changed, get returns an empty dict.
+ # In that case, we assume that it's zero (factory default)
+ try:
+ toget = path.replace('sample', 'enable')
+ # ZI like nesting inside dicts...
+ setting = daq.get(toget)[dev]['demods'][dmnum]['enable']['value'][0]
+ except KeyError:
+ setting = 0
+ streamsettings.append(setting)
+ daq.setInt(path.replace('sample', 'enable'), 1)
+
+ # We potentially subscribe several times to the same demodulator,
+ # but that should not be a problem
+ sweeper.subscribe(path)
+
+ sweeper.execute()
+ timeout = self._instrument.sweeper_timeout.get()
+ start = time.time()
+ while not sweeper.finished(): # Wait until the sweep is done/timeout
+ time.sleep(0.2) # Check every 200 ms whether the sweep is done
+ # Here we could read intermediate data via:
+ # data = sweeper.read(True)...
+ # and process it while the sweep is completing.
+ if (time.time() - start) > timeout:
+ # If for some reason the sweep is blocking, force the end of the
+ # measurement.
+ log.error("Sweep still not finished, forcing finish...")
+ # should exit function with error message instead of returning
+ sweeper.finish()
+
+ return_flat_dict = True
+ data = sweeper.read(return_flat_dict)
+
+ sweeper.unsubscribe('*')
+ for (state, sigstr) in zip(streamsettings, signals):
+ path = '/'.join(sigstr.split('/')[:-1])
+ daq.setInt(path.replace('sample', 'enable'), int(state))
+
+ return self._parsesweepdata(data)
+
+ def _parsesweepdata(self, sweepresult):
+ """
+ Parse the raw result of a sweep into just the data asked for by the
+ added sweeper signals. Used by Sweep.get.
+
+ Args:
+ sweepresult (dict): The dict returned by sweeper.read
+
+ Returns:
+ tuple: The requested signals in a tuple
+ """
+ trans = {'X': 'x', 'Y': 'y', 'Aux Input 1': 'auxin0',
+ 'Aux Input 2': 'auxin1', 'R': 'r', 'phase': 'phase',
+ 'Xrms': 'xpwr', 'Yrms': 'ypwr', 'Rrms': 'rpwr'}
+ returndata = []
+
+ for signal in self._instrument._sweeper_signals:
+ path = '/'.join(signal.split('/')[:-1])
+ attr = signal.split('/')[-1]
+ data = sweepresult[path][0][0][trans[attr]]
+ returndata.append(data)
+
+ return tuple(returndata)
+
+
+class Scope(MultiParameter):
+ """
+ Parameter class for the ZI UHF-LI Scope Channel 1
+
+ The .get method launches an acquisition and returns a tuple of two
+ np.arrays
+ FFT mode is NOT supported.
+
+ Attributes:
+ names (tuple): Tuple of strings containing the names of the sweep
+ signals (to be measured)
+ units (tuple): Tuple of strings containg the units of the signals
+ shapes (tuple): Tuple of tuples each containing the Length of a
+ signal.
+ setpoints (tuple): Tuple of N copies of the sweep x-axis points,
+ where N is he number of measured signals
+ setpoint_names (tuple): Tuple of N identical strings with the name
+ of the sweep x-axis.
+ """
+ def __init__(self, name, instrument, **kwargs):
+ # The __init__ requires that we supply names and shapes,
+ # but there is no way to know what they could be known at this time.
+ # They are updated via build_scope.
+ super().__init__(name, names=('',), shapes=((1,),), **kwargs)
+ self._instrument = instrument
+
+ def prepare_scope(self):
+ """
+ Prepare the scope for a measurement. Must immediately preceed a
+ measurement.
+ """
+
+ log.info('Preparing the scope')
+
+ # A convenient reference
+ params = self._instrument.parameters
+
+ # First figure out what the user has asked for
+ chans = {1: (True, False), 2: (False, True), 3: (True, True)}
+ channels = chans[params['scope_channels'].get()]
+
+ npts = params['scope_length'].get()
+ # Find out whether segments are enabled
+ if params['scope_segments'].get() == 'ON':
+ segs = params['scope_segments_count'].get()
+ else:
+ segs = 1
+
+ inputunits = {'Signal Input 1': 'V',
+ 'Signal Input 2': 'V',
+ 'Trig Input 1': 'V',
+ 'Trig Input 2': 'V',
+ 'Aux Output 1': 'V',
+ 'Aux Output 2': 'V',
+ 'Aux Output 3': 'V',
+ 'Aux Output 4': 'V',
+ 'Aux In 1 Ch 1': 'V',
+ 'Aux In 1 Ch 2': 'V',
+ 'Osc phi Demod 4': '°',
+ 'osc phi Demod 8': '°',
+ 'AU Cartesian 1': 'arb. un.',
+ 'AU Cartesian 2': 'arb. un',
+ 'AU Polar 1': 'arb. un.',
+ 'AU Polar 2': 'arb. un.',
+ }
+
+ #TODO: what are good names?
+ inputnames = {'Signal Input 1': 'Sig. In 1',
+ 'Signal Input 2': 'Sig. In 2',
+ 'Trig Input 1': 'Trig. In 1',
+ 'Trig Input 2': 'Trig. In 2',
+ 'Aux Output 1': 'Aux. Out 1',
+ 'Aux Output 2': 'Aux. Out 2',
+ 'Aux Output 3': 'Aux. Out 3',
+ 'Aux Output 4': 'Aux. Out 4',
+ 'Aux In 1 Ch 1': 'Aux. In 1 Ch 1',
+ 'Aux In 1 Ch 2': 'Aux. In 1 Ch 2',
+ 'Osc phi Demod 4': 'Demod. 4 Phase',
+ 'osc phi Demod 8': 'Demod. 8 Phase',
+ 'AU Cartesian 1': 'AU Cartesian 1',
+ 'AU Cartesian 2': 'AU Cartesian 2',
+ 'AU Polar 1': 'AU Polar 1',
+ 'AU Polar 2': 'AU Polar 2',
+ }
+ # Make the basic setpoints (the x-axis)
+ duration = params['scope_duration'].get()
+ delay = params['scope_trig_delay'].get()
+ starttime = params['scope_trig_reference'].get()*0.01*duration + delay
+ stoptime = starttime + duration
+
+ setpointlist = tuple(np.linspace(starttime, stoptime, npts)) # x-axis
+ spname = 'Time'
+ namestr = "scope_channel{}_input".format(1)
+ name1 = inputnames[params[namestr].get()]
+ unit1 = inputunits[params[namestr].get()]
+ namestr = "scope_channel{}_input".format(2)
+ name2 = inputnames[params[namestr].get()]
+ unit2 = inputunits[params[namestr].get()]
+
+ self.setpoints = ((tuple(range(segs)), (setpointlist,)*segs),)*2
+ #self.setpoints = ((setpointlist,)*segs,)*2
+ self.setpoint_names = (('Segments', 'Time'), ('Segments', 'Time'))
+ self.names = (name1, name2)
+ self.units = (unit1, unit2)
+ self.labels = ('Scope channel 1', 'Scope channel 2')
+ self.shapes = ((segs, npts), (segs, npts))
+
+ self._instrument.daq.sync()
+ self._instrument.scope_correctly_built = True
+
+ def get(self):
+ """
+ Acquire data from the scope.
+
+ Returns:
+ tuple: Tuple of two n X m arrays where n is the number of segments
+ and m is the number of points in the scope trace.
+
+ Raises:
+ ValueError: If the scope has not been prepared by running the
+ prepare_scope function.
+ """
+ if not self._instrument.scope_correctly_built:
+ raise ValueError('Scope not properly prepared. Please run '
+ 'prepare_scope before measuring.')
+
+ # A convenient reference
+ params = self._instrument.parameters
+ #
+ chans = {1: (True, False), 2: (False, True), 3: (True, True)}
+ channels = chans[params['scope_channels'].get()]
+
+ if params['scope_trig_holdoffmode'].get_latest() == 'events':
+ raise NotImplementedError('Scope trigger holdoff in number of '
+ 'events not supported. Please specify '
+ 'holdoff in seconds.')
+
+ #######################################################
+ # The following steps SEEM to give the correct result
+
+ # Make sure all settings have taken effect
+ self._instrument.daq.sync()
+
+ # Calculate the time needed for the measurement. We often have failed
+ # measurements, so a timeout is needed.
+ if params['scope_segments'].get() == 'ON':
+ segs = params['scope_segments_count'].get()
+ else:
+ segs = 1
+ deadtime = params['scope_trig_holdoffseconds'].get_latest()
+ # We add one second to account for latencies and random delays
+ meas_time = segs*(params['scope_duration'].get()+deadtime)+1
+ npts = params['scope_length'].get()
+
+ # Create a new scopeModule instance (TODO: Why a new instance?)
+ scope = self._instrument.daq.scopeModule()
+
+ # Subscribe to the relevant... publisher?
+ scope.subscribe('/{}/scopes/0/wave'.format(self._instrument.device))
+
+ # Start the scope triggering/acquiring
+ params['scope_runstop'].set('run')
+
+ log.info('[*] Starting ZI scope acquisition.')
+ # Start something... hauling data from the scopeModule?
+ scope.execute()
+
+ starttime = time.time()
+ timedout = False
+
+ while scope.progress() < 1:
+ time.sleep(0.1) # This while+sleep is how ZI engineers do it
+ if (time.time()-starttime) > meas_time:
+ scope.finish() # Force break the acquisition
+ timedout = True
+ log.warning('[-] ZI Scope acquisition did not finish correctly')
+ break
+
+ # Stop the scope from running
+ params['scope_runstop'].set('stop')
+
+ if not timedout:
+ log.info('[+] ZI scope acquisition completed OK')
+ rawdata = scope.read()
+ data = self._scopedataparser(rawdata, self._instrument.device,
+ npts, segs, channels)
+ else:
+ rawdata = None
+ data = (None, None)
+
+ # kill the scope instance
+ scope.clear()
+
+ return data
+
+ @staticmethod
+ def _scopedataparser(rawdata, deviceID, scopelength, segments, channels):
+ """
+ Cast the scope return value dict into a tuple.
+
+ Args:
+ rawdata (dict): The return of scopeModule.read()
+ deviceID (str): The device ID string of the instrument.
+ scopelength (int): The length of each segment
+ segments (int): The number of segments
+ channels (tuple): Tuple of two bools controlling what data to return
+ (True, False) will return data for channel 1 etc.
+
+ Returns:
+ tuple: A 2-tuple of either None or np.array with dimensions
+ segments x scopelength.
+ """
+
+ data = rawdata['{}'.format(deviceID)]['scopes']['0']['wave'][0][0]
+ if channels[0]:
+ ch1data = data['wave'][0].reshape(segments, scopelength)
+ else:
+ ch1data = None
+ if channels[1]:
+ ch2data = data['wave'][1].reshape(segments, scopelength)
+ else:
+ ch2data = None
+
+ return (ch1data, ch2data)
+
+class ZIUHFLI(Instrument):
+ """
+ QCoDeS driver for ZI UHF-LI.
+
+ Currently implementing demodulator settings and the sweeper functionality.
+
+ Requires ZI Lab One software to be installed on the computer running QCoDeS.
+ Furthermore, the Data Server and Web Server must be running and a connection
+ between the two must be made.
+
+ TODOs:
+ * Add zoom-FFT
+ """
+
+ def __init__(self, name, device_ID, **kwargs):
+ """
+ Create an instance of the instrument.
+
+ Args:
+ name (str): The internal QCoDeS name of the instrument
+ device_ID (str): The device name as listed in the web server.
+ api_level (int): Compatibility mode of the API interface. Must be 5
+ for the UHF.
+ """
+
+ super().__init__(name, **kwargs)
+ self.api_level = 5
+ zisession = zhinst.utils.create_api_session(device_ID, self.api_level)
+ (self.daq, self.device, self.props) = zisession
+
+ self.daq.setDebugLevel(3)
+ # create (instantiate) an instance of each module we will use
+ self.sweeper = self.daq.sweep()
+ self.sweeper.set('sweep/device', self.device)
+ self.scope = self.daq.scopeModule()
+
+ ########################################
+ # INSTRUMENT PARAMETERS
+
+ ########################################
+ # Oscillators
+ for oscs in range(1,3):
+ self.add_parameter('oscillator{}_freq'.format(oscs),
+ label='Frequency of oscillator {}'.format(oscs),
+ unit='Hz',
+ set_cmd=partial(self._setter, 'oscs',
+ oscs-1, 1, 'freq'),
+ get_cmd=partial(self._getter, 'oscs',
+ oscs-1, 1, 'freq'),
+ vals=vals.Numbers(0, 600e6))
+
+ ########################################
+ # DEMODULATOR PARAMETERS
+
+ for demod in range(1, 9):
+ self.add_parameter('demod{}_order'.format(demod),
+ label='Filter order',
+ get_cmd=partial(self._getter, 'demods',
+ demod-1, 0, 'order'),
+ set_cmd=partial(self._setter, 'demods',
+ demod-1, 0, 'order'),
+ vals=vals.Ints(1, 8)
+ )
+
+ self.add_parameter('demod{}_harmonic'.format(demod),
+ label=('Reference frequency multiplication' +
+ ' factor'),
+ get_cmd=partial(self._getter, 'demods',
+ demod-1, 1, 'harmonic'),
+ set_cmd=partial(self._setter, 'demods',
+ demod-1, 1, 'harmonic'),
+ vals=vals.Ints(1, 999)
+ )
+
+ self.add_parameter('demod{}_timeconstant'.format(demod),
+ label='Filter time constant',
+ get_cmd=partial(self._getter, 'demods',
+ demod-1, 1, 'timeconstant'),
+ set_cmd=partial(self._setter, 'demods',
+ demod-1, 1, 'timeconstant'),
+ unit='s'
+ )
+
+ self.add_parameter('demod{}_samplerate'.format(demod),
+ label='Sample rate',
+ get_cmd=partial(self._getter, 'demods',
+ demod-1, 1, 'rate'),
+ set_cmd=partial(self._setter, 'demods',
+ demod-1, 1, 'rate'),
+ unit='Sa/s',
+ docstring="""
+ Note: the value inserted by the user
+ may be approximated to the
+ nearest value supported by the
+ instrument.
+ """)
+
+ self.add_parameter('demod{}_phaseshift'.format(demod),
+ label='Phase shift',
+ unit='degrees',
+ get_cmd=partial(self._getter, 'demods',
+ demod-1, 1, 'phaseshift'),
+ set_cmd=partial(self._setter, 'demods',
+ demod-1, 1, 'phaseshift')
+ )
+
+ # val_mapping for the demodX_signalin parameter
+ dmsigins = {'Sig In 1': 0,
+ 'Sig In 2': 1,
+ 'Trigger 1': 2,
+ 'Trigger 2': 3,
+ 'Aux Out 1': 4,
+ 'Aux Out 2': 5,
+ 'Aux Out 3': 6,
+ 'Aux Out 4': 7,
+ 'Aux In 1': 8,
+ 'Aux In 2': 9,
+ 'Phi Demod 4': 10,
+ 'Phi Demod 8': 11}
+
+ self.add_parameter('demod{}_signalin'.format(demod),
+ label='Signal input',
+ get_cmd=partial(self._getter, 'demods',
+ demod-1, 0,'adcselect'),
+ set_cmd=partial(self._setter, 'demods',
+ demod-1, 0, 'adcselect'),
+ val_mapping=dmsigins,
+ vals=vals.Enum(*list(dmsigins.keys()))
+ )
+
+ self.add_parameter('demod{}_sinc'.format(demod),
+ label='Sinc filter',
+ get_cmd=partial(self._getter, 'demods',
+ demod-1, 0, 'sinc'),
+ set_cmd=partial(self._setter, 'demods',
+ demod-1, 0, 'sinc'),
+ val_mapping={'ON': 1, 'OFF': 0},
+ vals=vals.Enum('ON', 'OFF')
+ )
+
+ self.add_parameter('demod{}_streaming'.format(demod),
+ label='Data streaming',
+ get_cmd=partial(self._getter, 'demods',
+ demod-1, 0, 'enable'),
+ set_cmd=partial(self._setter, 'demods',
+ demod-1, 0, 'enable'),
+ val_mapping={'ON': 1, 'OFF': 0},
+ vals=vals.Enum('ON', 'OFF')
+ )
+
+ dmtrigs = {'Continuous': 0,
+ 'Trigger in 3 Rise': 1,
+ 'Trigger in 3 Fall': 2,
+ 'Trigger in 3 Both': 3,
+ 'Trigger in 3 High': 32,
+ 'Trigger in 3 Low': 16,
+ 'Trigger in 4 Rise': 4,
+ 'Trigger in 4 Fall': 8,
+ 'Trigger in 4 Both': 12,
+ 'Trigger in 4 High': 128,
+ 'Trigger in 4 Low': 64,
+ 'Trigger in 3|4 Rise': 5,
+ 'Trigger in 3|4 Fall': 10,
+ 'Trigger in 3|4 Both': 15,
+ 'Trigger in 3|4 High': 160,
+ 'Trigger in 3|4 Low': 80}
+
+ self.add_parameter('demod{}_trigger'.format(demod),
+ label='Trigger',
+ get_cmd=partial(self._getter, 'demods',
+ demod-1, 0, 'trigger'),
+ set_cmd=partial(self._setter, 'demods',
+ demod-1, 0, 'trigger'),
+ val_mapping=dmtrigs,
+ vals=vals.Enum(*list(dmtrigs.keys()))
+ )
+
+ ########################################
+ # SIGNAL INPUTS
+
+ for sigin in range(1, 3):
+
+ self.add_parameter('signal_input{}_range'.format(sigin),
+ label='Input range',
+ set_cmd=partial(self._setter, 'sigins',
+ sigin-1, 1, 'range'),
+ get_cmd=partial(self._getter, 'sigins',
+ sigin-1, 1, 'range'),
+ unit='V')
+
+ self.add_parameter('signal_input{}_scaling'.format(sigin),
+ label='Input scaling',
+ set_cmd=partial(self._setter, 'sigins',
+ sigin-1, 1, 'scaling'),
+ get_cmd=partial(self._getter, 'sigins',
+ sigin-1, 1, 'scaling'),
+ )
+
+ self.add_parameter('signal_input{}_AC'.format(sigin),
+ label='AC coupling',
+ set_cmd=partial(self._setter,'sigins',
+ sigin-1, 0, 'ac'),
+ get_cmd=partial(self._getter, 'sigins',
+ sigin-1, 0, 'ac'),
+ val_mapping={'ON': 1, 'OFF': 0},
+ vals=vals.Enum('ON', 'OFF')
+ )
+
+ self.add_parameter('signal_input{}_impedance'.format(sigin),
+ label='Input impedance',
+ set_cmd=partial(self._setter, 'sigins',
+ sigin-1, 0, 'imp50'),
+ get_cmd=partial(self._getter, 'sigins',
+ sigin-1, 0, 'imp50'),
+ val_mapping={50: 1, 1000: 0},
+ vals=vals.Enum(50, 1000)
+ )
+
+ sigindiffs = {'Off': 0, 'Inverted': 1, 'Input 1 - Input 2': 2,
+ 'Input 2 - Input 1': 3}
+ self.add_parameter('signal_input{}_diff'.format(sigin),
+ label='Input signal subtraction',
+ set_cmd=partial(self._setter, 'sigins',
+ sigin-1, 0, 'diff'),
+ get_cmd=partial(self._getter, 'sigins',
+ sigin-1, 0, 'diff'),
+ val_mapping=sigindiffs,
+ vals=vals.Enum(*list(sigindiffs.keys())))
+
+ ########################################
+ # SIGNAL OUTPUTS
+ outputamps = {1: 'amplitudes/3', 2: 'amplitudes/7'}
+ outputampenable = {1: 'enables/3', 2: 'enables/7'}
+
+ for sigout in range(1,3):
+
+ self.add_parameter('signal_output{}_on'.format(sigout),
+ label='Turn signal output on and off.',
+ set_cmd=partial(self._sigout_setter,
+ sigout-1, 0, 'on'),
+ get_cmd=partial(self._sigout_getter,
+ sigout-1, 0, 'on'),
+ val_mapping={'ON': 1, 'OFF': 0},
+ vals=vals.Enum('ON', 'OFF') )
+
+ self.add_parameter('signal_output{}_imp50'.format(sigout),
+ label='Switch to turn on 50 Ohm impedance',
+ set_cmd=partial(self._sigout_setter,
+ sigout-1, 0, 'imp50'),
+ get_cmd=partial(self._sigout_getter,
+ sigout-1, 0, 'imp50'),
+ val_mapping={'ON': 1, 'OFF': 0},
+ vals=vals.Enum('ON', 'OFF') )
+
+ self.add_parameter('signal_output{}_amplitude'.format(sigout),
+ label='Signal output amplitude',
+ set_cmd=partial(self._sigout_setter,
+ sigout-1, 1, outputamps[sigout]),
+ get_cmd=partial(self._sigout_getter,
+ sigout-1, 1, outputamps[sigout]),
+ unit='V')
+
+ self.add_parameter('signal_output{}_ampdef'.format(sigout),
+ parameter_class=ManualParameter,
+ initial_value='Vpk',
+ label="Signal output amplitude's definition",
+ unit='V',
+ vals=vals.Enum('Vpk','Vrms', 'dBm'))
+
+ self.add_parameter('signal_output{}_range'.format(sigout),
+ label='Signal output range',
+ set_cmd=partial(self._sigout_setter,
+ sigout-1, 1, 'range'),
+ get_cmd=partial(self._sigout_getter,
+ sigout-1, 1, 'range'),
+ vals=vals.Enum(0.075, 0.15, 0.75, 1.5))
+
+ self.add_parameter('signal_output{}_offset'.format(sigout),
+ label='Signal output offset',
+ set_cmd=partial(self._sigout_setter,
+ sigout-1, 1, 'offset'),
+ get_cmd=partial(self._sigout_getter,
+ sigout-1, 1, 'offset'),
+ vals=vals.Numbers(-1.5, 1.5),
+ unit='V')
+
+ self.add_parameter('signal_output{}_autorange'.format(sigout),
+ label='Enable signal output range.',
+ set_cmd=partial(self._sigout_setter,
+ sigout-1, 0, 'autorange'),
+ get_cmd=partial(self._sigout_getter,
+ sigout-1, 0, 'autorange'),
+ val_mapping={'ON': 1, 'OFF': 0},
+ vals=vals.Enum('ON', 'OFF') )
+
+ self.add_parameter('signal_output{}_enable'.format(sigout),
+ label="Enable signal output's amplitude.",
+ set_cmd=partial(self._sigout_setter,
+ sigout-1, 0,
+ outputampenable[sigout]),
+ get_cmd=partial(self._sigout_getter,
+ sigout-1, 0,
+ outputampenable[sigout]),
+ val_mapping={'ON': 1, 'OFF': 0},
+ vals=vals.Enum('ON', 'OFF') )
+
+
+
+ ########################################
+ # SWEEPER PARAMETERS
+
+ self.add_parameter('sweeper_BWmode',
+ label='Sweeper bandwidth control mode',
+ set_cmd=partial(self._sweep_setter,
+ 'sweep/bandwidthcontrol'),
+ get_cmd=partial(self._sweep_getter,
+ 'sweep/bandwidthcontrol'),
+ val_mapping={'auto': 2, 'fixed': 1, 'current': 0},
+ docstring="""
+ For each sweep point, the demodulator
+ filter bandwidth (time constant) may
+ be either set automatically, be the
+ current demodulator bandwidth or be
+ a fixed number; the sweeper_BW
+ parameter.
+ """
+ )
+
+ self.add_parameter('sweeper_BW',
+ label='Fixed bandwidth sweeper bandwidth (NEP)',
+ set_cmd=partial(self._sweep_setter,
+ 'sweep/bandwidth'),
+ get_cmd=partial(self._sweep_getter,
+ 'sweep/bandwidth'),
+ docstring="""
+ This is the NEP bandwidth used by the
+ sweeper if sweeper_BWmode is set to
+ 'fixed'. If sweeper_BWmode is either
+ 'auto' or 'current', this value is
+ ignored.
+ """
+ )
+
+ self.add_parameter('sweeper_start',
+ label='Start value of the sweep',
+ set_cmd=partial(self._sweep_setter,
+ 'sweep/start'),
+ get_cmd=partial(self._sweep_getter,
+ 'sweep/start'),
+ vals=vals.Numbers(0, 600e6))
+
+ self.add_parameter('sweeper_stop',
+ label='Stop value of the sweep',
+ set_cmd=partial(self._sweep_setter,
+ 'sweep/stop'),
+ get_cmd=partial(self._sweep_getter,
+ 'sweep/stop'),
+ vals=vals.Numbers(0, 600e6))
+
+ self.add_parameter('sweeper_samplecount',
+ label='Length of the sweep (pts)',
+ set_cmd=partial(self._sweep_setter,
+ 'sweep/samplecount'),
+ get_cmd=partial(self._sweep_getter,
+ 'sweep/samplecount'),
+ vals=vals.Ints(0, 100000))
+
+ # val_mapping for sweeper_param parameter
+ sweepparams = {'Aux Out 1 Offset': 'auxouts/0/offset',
+ 'Aux Out 2 Offset': 'auxouts/1/offset',
+ 'Aux Out 3 Offset': 'auxouts/2/offset',
+ 'Aux Out 4 Offset': 'auxouts/3/offset',
+ 'Demod 1 Phase Shift': 'demods/0/phaseshift',
+ 'Demod 2 Phase Shift': 'demods/1/phaseshift',
+ 'Demod 3 Phase Shift': 'demods/2/phaseshift',
+ 'Demod 4 Phase Shift': 'demods/3/phaseshift',
+ 'Demod 5 Phase Shift': 'demods/4/phaseshift',
+ 'Demod 6 Phase Shift': 'demods/5/phaseshift',
+ 'Demod 7 Phase Shift': 'demods/6/phaseshift',
+ 'Demod 8 Phase Shift': 'demods/7/phaseshift',
+ 'Osc 1 Frequency': 'oscs/0/freq',
+ 'Osc 2 Frequency': 'oscs/1/freq',
+ 'Output 1 Amplitude 4': 'sigouts/0/amplitudes/3',
+ 'Output 1 Offset': 'sigouts/0/offset',
+ 'Output 2 Amplitude 8': 'sigouts/1/amplitudes/7',
+ 'Output 2 Offset': 'sigouts/1/offset'
+ }
+
+ self.add_parameter('sweeper_param',
+ label='Parameter to sweep (sweep x-axis)',
+ set_cmd=partial(self._sweep_setter,
+ 'sweep/gridnode'),
+ val_mapping=sweepparams,
+ get_cmd=partial(self._sweep_getter,
+ 'sweep/gridnode'),
+ vals=vals.Enum(*list(sweepparams.keys()))
+ )
+
+ # val_mapping for sweeper_units parameter
+ sweepunits = {'Aux Out 1 Offset': 'V',
+ 'Aux Out 2 Offset': 'V',
+ 'Aux Out 3 Offset': 'V',
+ 'Aux Out 4 Offset': 'V',
+ 'Demod 1 Phase Shift': 'degrees',
+ 'Demod 2 Phase Shift': 'degrees',
+ 'Demod 3 Phase Shift': 'degrees',
+ 'Demod 4 Phase Shift': 'degrees',
+ 'Demod 5 Phase Shift': 'degrees',
+ 'Demod 6 Phase Shift': 'degrees',
+ 'Demod 7 Phase Shift': 'degrees',
+ 'Demod 8 Phase Shift': 'degrees',
+ 'Osc 1 Frequency': 'Hz',
+ 'Osc 2 Frequency': 'Hz',
+ 'Output 1 Amplitude 4': 'V',
+ 'Output 1 Offset': 'V',
+ 'Output 2 Amplitude 8': 'V',
+ 'Output 2 Offset': 'V'
+ }
+
+ self.add_parameter('sweeper_units',
+ label='Units of sweep x-axis',
+ get_cmd=self.sweeper_param.get,
+ get_parser=lambda x:sweepunits[x])
+
+ # val_mapping for sweeper_mode parameter
+ sweepmodes = {'Sequential': 0,
+ 'Binary': 1,
+ 'Biderectional': 2,
+ 'Reverse': 3}
+
+ self.add_parameter('sweeper_mode',
+ label='Sweep mode',
+ set_cmd=partial(self._sweep_setter,
+ 'sweep/scan'),
+ get_cmd=partial(self._sweep_getter, 'sweep/scan'),
+ val_mapping=sweepmodes,
+ vals=vals.Enum(*list(sweepmodes))
+ )
+
+ self.add_parameter('sweeper_order',
+ label='Sweeper filter order',
+ set_cmd=partial(self._sweep_setter,
+ 'sweep/order'),
+ get_cmd=partial(self._sweep_getter,
+ 'sweep/order'),
+ vals=vals.Ints(1, 8),
+ docstring="""
+ This value is invoked only when the
+ sweeper_BWmode is set to 'fixed'.
+ """)
+
+ self.add_parameter('sweeper_settlingtime',
+ label=('Minimal settling time for the ' +
+ 'sweeper'),
+ set_cmd=partial(self._sweep_setter,
+ 'sweep/settling/time'),
+ get_cmd=partial(self._sweep_getter,
+ 'sweep/settling/time'),
+ vals=vals.Numbers(0),
+ unit='s',
+ docstring="""
+ This is the minimal waiting time
+ at each point during a sweep before the
+ data acquisition starts. Note that the
+ filter settings may result in a longer
+ actual waiting/settling time.
+ """
+ )
+
+ self.add_parameter('sweeper_inaccuracy',
+ label='Demodulator filter settling inaccuracy',
+ set_cmd=partial(self._sweep_setter,
+ 'sweep/settling/inaccuracy'),
+ docstring="""
+ Demodulator filter settling inaccuracy
+ defining the wait time between a sweep
+ parameter change and recording of the
+ next sweep point. The settling time is
+ calculated as the time required to attain
+ the specified remaining proportion [1e-13,
+ 0.1] of an incoming step function. Typical
+ inaccuracy values: 10m for highest sweep
+ speed for large signals, 100u for precise
+ amplitude measurements, 100n for precise
+ noise measurements. Depending on the
+ order of the demodulator filter the settling
+ inaccuracy will define the number of filter
+ time constants the sweeper has to wait. The
+ maximum between this value and the settling
+ time is taken as wait time until the next
+ sweep point is recorded.
+ """
+ )
+
+ self.add_parameter('sweeper_settlingtc',
+ label='Sweep filter settling time',
+ get_cmd=partial(self._sweep_getter,
+ 'sweep/settling/tc'),
+ unit='',
+ docstring="""This settling time is in units of
+ the filter time constant."""
+ )
+
+ self.add_parameter('sweeper_averaging_samples',
+ label=('Minimal no. of samples to average at ' +
+ 'each sweep point'),
+ set_cmd=partial(self._sweep_setter,
+ 'sweep/averaging/sample'),
+ get_cmd=partial(self._sweep_getter,
+ 'sweep/averaging/sample'),
+ vals=vals.Ints(1),
+ docstring="""
+ The actual number of samples is the
+ maximum of this value and the
+ sweeper_averaging_time times the
+ relevant sample rate.
+ """
+ )
+
+ self.add_parameter('sweeper_averaging_time',
+ label=('Minimal averaging time'),
+ set_cmd=partial(self._sweep_setter,
+ 'sweep/averaging/tc'),
+ get_cmd=partial(self._sweep_getter,
+ 'sweep/averaging/tc'),
+ unit='s',
+ docstring="""
+ The actual number of samples is the
+ maximum of this value times the
+ relevant sample rate and the
+ sweeper_averaging_samples."""
+ )
+
+ self.add_parameter('sweeper_xmapping',
+ label='Sweeper x mapping',
+ set_cmd=partial(self._sweep_setter,
+ 'sweep/xmapping'),
+ get_cmd=partial(self._sweep_getter,
+ 'sweep/xmapping'),
+ val_mapping={'lin': 0, 'log': 1}
+ )
+
+ self.add_parameter('sweeper_sweeptime',
+ label='Expected sweep time',
+ unit='s',
+ get_cmd=self._get_sweep_time)
+
+ self.add_parameter('sweeper_timeout',
+ label='Sweep timeout',
+ unit='s',
+ initial_value=600,
+ parameter_class=ManualParameter)
+
+ ########################################
+ # THE SWEEP ITSELF
+ self.add_parameter('Sweep',
+ parameter_class=Sweep,
+ )
+
+ # A "manual" parameter: a list of the signals for the sweeper
+ # to subscribe to
+ self._sweeper_signals = []
+
+ # This is the dictionary keeping track of the sweeper settings
+ # These are the default settings
+ self._sweepdict = {'start': 1e6,
+ 'stop': 10e6,
+ 'samplecount': 25,
+ 'bandwidthcontrol': 1, # fixed mode
+ 'bandwidth': 50,
+ 'gridnode': 'oscs/0/freq',
+ 'scan': 0, # sequential scan
+ 'order': 1,
+ 'settling/time': 1e-6,
+ 'settling/inaccuracy': 10e-3,
+ 'averaging/sample': 25,
+ 'averaging/tc': 100e-3,
+ 'xmapping': 0, # linear
+ }
+ # Set up the sweeper with the above settings
+ self.Sweep.build_sweep()
+
+ ########################################
+ # SCOPE PARAMETERS
+ # default parameters:
+
+ # This parameter corresponds to the Run/Stop button in the GUI
+ self.add_parameter('scope_runstop',
+ label='Scope run state',
+ set_cmd=partial(self._setter, 'scopes', 0, 0,
+ 'enable'),
+ get_cmd=partial(self._getter, 'scopes', 0, 0,
+ 'enable'),
+ val_mapping={'run': 1, 'stop': 0},
+ vals=vals.Enum('run', 'stop'),
+ docstring=('This parameter corresponds to the '
+ 'run/stop button in the GUI.'))
+
+ self.add_parameter('scope_mode',
+ label="Scope's mode: time or frequency domain.",
+ set_cmd=partial(self._scope_setter, 1, 0,
+ 'mode'),
+ get_cmd=partial(self._scope_getter, 'mode'),
+ val_mapping={'Time Domain': 1,
+ 'Freq Domain FFT': 3},
+ vals=vals.Enum('Time Domain', 'Freq Domain FFT')
+ )
+
+ # 1: Channel 1 on, Channel 2 off.
+ # 2: Channel 1 off, Channel 2 on,
+ # 3: Channel 1 on, Channel 2 on.
+ self.add_parameter('scope_channels',
+ label='Recorded scope channels',
+ set_cmd=partial(self._scope_setter, 0, 0,
+ 'channel'),
+ get_cmd=partial(self._getter, 'scopes', 0,
+ 0, 'channel'),
+ vals=vals.Enum(1, 2, 3)
+ )
+
+ self._samplingrate_codes = {'1.80 Ghz': 0,
+ '900 MHz': 1,
+ '450 MHz': 2,
+ '225 MHz': 3,
+ '113 MHz': 4,
+ '56.2 MHz': 5,
+ '28.1 MHz': 6,
+ '14.0 MHz': 7,
+ '7.03 MHz': 8,
+ '3.50 MHz': 9,
+ '1.75 MHz': 10,
+ '880 kHz': 11,
+ '440 kHz': 12,
+ '220 kHz': 13,
+ '110 kHz': 14,
+ '54.9 kHz': 15,
+ '27.5 kHz': 16}
+
+ self.add_parameter('scope_samplingrate',
+ label="Scope's sampling rate",
+ set_cmd=partial(self._scope_setter, 0, 0,
+ 'time'),
+ get_cmd=partial(self._getter, 'scopes', 0,
+ 0, 'time'),
+ val_mapping=self._samplingrate_codes,
+ vals=vals.Enum(*list(self._samplingrate_codes.keys()))
+ )
+
+ self.add_parameter('scope_length',
+ label="Length of scope trace (pts)",
+ set_cmd=partial(self._scope_setter, 0, 1,
+ 'length'),
+ get_cmd=partial(self._getter, 'scopes', 0,
+ 1, 'length'),
+ vals=vals.Numbers(4096, 128000000),
+ get_parser=int
+ )
+
+ self.add_parameter('scope_duration',
+ label="Scope trace duration",
+ set_cmd=partial(self._scope_setter, 0, 0,
+ 'duration'),
+ get_cmd=partial(self._scope_getter,
+ 'duration'),
+ vals=vals.Numbers(2.27e-6,4.660e3),
+ unit='s'
+ )
+
+ # Map the possible input sources to LabOne's IDs.
+ # The IDs can be seen in log file of LabOne UI
+ inputselect = {'Signal Input 1': 0,
+ 'Signal Input 2': 1,
+ 'Trig Input 1': 2,
+ 'Trig Input 2': 3,
+ 'Aux Output 1': 4,
+ 'Aux Output 2': 5,
+ 'Aux Output 3': 6,
+ 'Aux Output 4': 7,
+ 'Aux In 1 Ch 1': 8,
+ 'Aux In 1 Ch 2': 9,
+ 'Osc phi Demod 4': 10,
+ 'Osc phi Demod 8': 11,
+ 'AU Cartesian 1': 112,
+ 'AU Cartesian 2': 113,
+ 'AU Polar 1': 128,
+ 'AU Polar 2': 129,
+ }
+ # Add all 8 demodulators and their respective parameters
+ # to inputselect as well.
+ # Numbers correspond to LabOne IDs, taken from UI log.
+ for demod in range(1,9):
+ inputselect['Demod {} X'.format(demod)] = 15+demod
+ inputselect['Demod {} Y'.format(demod)] = 31+demod
+ inputselect['Demod {} R'.format(demod)] = 47+demod
+ inputselect['Demod {} Phase'.format(demod)] = 63+demod
+
+ for channel in range(1,3):
+ self.add_parameter('scope_channel{}_input'.format(channel),
+ label=("Scope's channel {}".format(channel) +
+ " input source"),
+ set_cmd=partial(self._scope_setter, 0, 0,
+ ('channels/{}/'.format(channel-1) +
+ 'inputselect')),
+ get_cmd=partial(self._getter, 'scopes', 0, 0,
+ ('channels/{}/'.format(channel-1) +
+ 'inputselect')),
+ val_mapping=inputselect,
+ vals=vals.Enum(*list(inputselect.keys()))
+ )
+
+ self.add_parameter('scope_average_weight',
+ label="Scope Averages",
+ set_cmd=partial(self._scope_setter, 1, 0,
+ 'averager/weight'),
+ get_cmd=partial(self._scope_getter,
+ 'averager/weight'),
+ vals=vals.Numbers(min_value=1)
+ )
+
+ self.add_parameter('scope_trig_enable',
+ label="Enable triggering for scope readout",
+ set_cmd=partial(self._setter, 'scopes', 0,
+ 0, 'trigenable'),
+ get_cmd=partial(self._getter, 'scopes', 0,
+ 0, 'trigenable'),
+ val_mapping={'ON': 1, 'OFF': 0},
+ vals=vals.Enum('ON', 'OFF')
+ )
+
+ self.add_parameter('scope_trig_signal',
+ label="Trigger signal source",
+ set_cmd=partial(self._setter, 'scopes', 0,
+ 0, 'trigchannel'),
+ get_cmd=partial(self._getter, 'scopes', 0,
+ 0, 'trigchannel'),
+ val_mapping=inputselect,
+ vals=vals.Enum(*list(inputselect.keys()))
+ )
+
+ slopes = {'None': 0, 'Rise': 1, 'Fall': 2, 'Both': 3}
+
+ self.add_parameter('scope_trig_slope',
+ label="Scope's triggering slope",
+ set_cmd=partial(self._setter, 'scopes', 0,
+ 0, 'trigslope'),
+ get_cmd=partial(self._getter, 'scopes', 0,
+ 0, 'trigslope'),
+ val_mapping=slopes,
+ vals=vals.Enum(*list(slopes.keys()))
+ )
+
+ # TODO: figure out how value/percent works for the trigger level
+ self.add_parameter('scope_trig_level',
+ label="Scope trigger level",
+ set_cmd=partial(self._setter, 'scopes', 0,
+ 1, 'triglevel'),
+ get_cmd=partial(self._getter, 'scopes', 0,
+ 1, 'triglevel'),
+ vals=vals.Numbers()
+ )
+
+ self.add_parameter('scope_trig_hystmode',
+ label="Enable triggering for scope readout.",
+ set_cmd=partial(self._setter, 'scopes', 0,
+ 0, 'trighysteresis/mode'),
+ get_cmd=partial(self._getter, 'scopes', 0,
+ 0, 'trighysteresis/mode'),
+ val_mapping={'absolute': 0, 'relative': 1},
+ vals=vals.Enum('absolute', 'relative')
+ )
+
+ self.add_parameter('scope_trig_hystrelative',
+ label="Trigger hysteresis, relative value in %",
+ set_cmd=partial(self._setter, 'scopes', 0,
+ 1, 'trighysteresis/relative'),
+ get_cmd=partial(self._getter, 'scopes', 0,
+ 1, 'trighysteresis/relative'),
+ # val_mapping= lambda x: 0.01*x,
+ vals=vals.Numbers(0)
+ )
+
+ self.add_parameter('scope_trig_hystabsolute',
+ label="Trigger hysteresis, absolute value",
+ set_cmd=partial(self._setter, 'scopes', 0,
+ 1, 'trighysteresis/absolute'),
+ get_cmd=partial(self._getter, 'scopes', 0,
+ 1, 'trighysteresis/absolute'),
+ vals=vals.Numbers(0, 20)
+ )
+
+ triggates = {'Trigger In 3 High': 0, 'Trigger In 3 Low': 1,
+ 'Trigger In 4 High': 2, 'Trigger In 4 Low': 3}
+ self.add_parameter('scope_trig_gating_source',
+ label='Scope trigger gating source',
+ set_cmd=partial(self._setter, 'scopes', 0, 0,
+ 'triggate/inputselect'),
+ get_cmd=partial(self._getter, 'scopes', 0, 0,
+ 'triggate/inputselect'),
+ val_mapping=triggates,
+ vals=vals.Enum(*list(triggates.keys()))
+ )
+
+ self.add_parameter('scope_trig_gating_enable',
+ label='Scope trigger gating ON/OFF',
+ set_cmd=partial(self._setter, 'scopes', 0, 0,
+ 'triggate/enable'),
+ get_cmd=partial(self._getter, 'scopes', 0, 0,
+ 'triggate/enable'),
+ val_mapping = {'ON': 1, 'OFF': 0},
+ vals=vals.Enum('ON', 'OFF'))
+
+ # make this a slave parameter off scope_holdoff_seconds
+ # and scope_holdoff_events
+ self.add_parameter('scope_trig_holdoffmode',
+ label="Scope trigger holdoff mode",
+ set_cmd=partial(self._setter, 'scopes', 0,
+ 0, 'trigholdoffmode'),
+ get_cmd=partial(self._getter, 'scopes', 0,
+ 0, 'trigholdoffmode'),
+ val_mapping={'s': 0, 'events': 1},
+ vals=vals.Enum('s', 'events')
+ )
+
+ self.add_parameter('scope_trig_holdoffseconds',
+ label='Scope trigger holdoff',
+ set_cmd=partial(self._scope_setter, 0, 1,
+ 'trigholdoff'),
+ get_cmd=partial(self._getter, 'scopes', 0,
+ 1, 'trigholdoff'),
+ unit='s',
+ vals=vals.Numbers(20e-6, 10)
+ )
+
+ self.add_parameter('scope_trig_reference',
+ label='Scope trigger reference',
+ set_cmd=partial(self._scope_setter, 0, 1,
+ 'trigreference'),
+ get_cmd=partial(self._getter, 'scopes', 0,
+ 1, 'trigreference'),
+ vals=vals.Numbers(0, 100)
+ )
+
+ # TODO: add validation. What's the minimal/maximal delay?
+ self.add_parameter('scope_trig_delay',
+ label='Scope trigger delay',
+ set_cmd=partial(self._scope_setter, 0, 1,
+ 'trigdelay'),
+ get_cmd=partial(self._getter, 'scopes', 0, 1,
+ 'trigdelay'),
+ unit='s')
+
+ self.add_parameter('scope_segments',
+ label='Enable/disable segments',
+ set_cmd=partial(self._scope_setter, 0, 0,
+ 'segments/enable'),
+ get_cmd=partial(self._getter, 'scopes', 0,
+ 0, 'segments/enable'),
+ val_mapping={'OFF': 0, 'ON': 1},
+ vals=vals.Enum('ON', 'OFF')
+ )
+
+ self.add_parameter('scope_segments_count',
+ label='No. of segments returned by scope',
+ set_cmd=partial(self._setter, 'scopes', 0, 1,
+ 'segments/count'),
+ get_cmd=partial(self._getter, 'scopes', 0, 1,
+ 'segments/count'),
+ vals=vals.Ints(1, 32768),
+ get_parser=int
+ )
+
+ self.add_function('scope_reset_avg',
+ call_cmd=partial(self.scope.set,
+ 'scopeModule/averager/restart', 1),
+ )
+
+ ########################################
+ # THE SCOPE ITSELF
+ self.add_parameter('Scope',
+ parameter_class=Scope,
+ )
+
+
+ def _setter(self, module, number, mode, setting, value):
+ """
+ General function to set/send settings to the device.
+
+ The module (e.g demodulator, input, output,..) number is counted in a
+ zero indexed fashion.
+
+ Args:
+ module (str): The module (eg. demodulator, input, output, ..)
+ to set.
+ number (int): Module's index
+ mode (bool): Indicating whether we are setting an int or double
+ setting (str): The module's setting to set.
+ value (int/double): The value to set.
+ """
+
+ setstr = '/{}/{}/{}/{}'.format(self.device, module, number, setting)
+
+ if mode == 0:
+ self.daq.setInt(setstr, value)
+ if mode == 1:
+ self.daq.setDouble(setstr, value)
+
+ def _getter(self, module, number, mode, setting):
+ """
+ General get function for generic parameters. Note that some parameters
+ use more specialised setter/getters.
+
+ The module (e.g demodulator, input, output,..) number is counted in a
+ zero indexed fashion.
+
+ Args:
+ module (str): The module (eg. demodulator, input, output, ..)
+ we want to know the value of.
+ number (int): Module's index
+ mode (int): Indicating whether we are asking for an int or double.
+ 0: Int, 1: double.
+ setting (str): The module's setting to set.
+ returns:
+ inquered value
+
+ """
+
+ querystr = '/{}/{}/{}/{}'.format(self.device, module, number, setting)
+ if mode == 0:
+ value = self.daq.getInt(querystr)
+ if mode == 1:
+ value = self.daq.getDouble(querystr)
+
+ # Weird exception, samplingrate returns a string
+ return value
+
+ def _sigout_setter(self, number, mode, setting, value):
+ """
+ Function to set signal output's settings. A specific setter function is
+ needed as parameters depend on each other and need to be checked and
+ updated accordingly.
+
+ Args:
+ number (int):
+ mode (bool): Indicating whether we are asking for an int or double
+ setting (str): The module's setting to set.
+ value (Union[int, float]): The value to set the setting to.
+ """
+
+ # convenient reference
+ params = self.parameters
+
+ def amp_valid():
+ nonlocal value
+ toget = params['signal_output{}_ampdef'.format(number+1)]
+ ampdef_val = toget.get()
+ toget = params['signal_output{}_autorange'.format(number+1)]
+ autorange_val = toget.get()
+
+ if autorange_val == 'ON':
+ toget = params['signal_output{}_imp50'.format(number+1)]
+ imp50_val = toget.get()
+ imp50_dic = {'OFF': 1.5, 'ON': 0.75}
+ range_val = imp50_dic[imp50_val]
+
+ else:
+ so_range = params['signal_output{}_range'.format(number+1)].get()
+ range_val = round(so_range, 3)
+
+ amp_val_dict={'Vpk': lambda value: value,
+ 'Vrms': lambda value: value*sqrt(2),
+ 'dBm': lambda value: 10**((value-10)/20)
+ }
+
+ if -range_val < amp_val_dict[ampdef_val](value) > range_val:
+ raise ValueError('Signal Output:'
+ + ' Amplitude too high for chosen range.')
+ value = amp_val_dict[ampdef_val](value)
+
+ def offset_valid():
+ nonlocal value
+ nonlocal number
+ range_val = params['signal_output{}_range'.format(number+1)].get()
+ range_val = round(val, 3)
+ amp_val = params['signal_output{}_amplitude'.format(number+1)].get()
+ amp_val = round(amp_val, 3)
+ toget = params['signal_output{}_autorange'.format(number+1)]
+ autorange_val = toget.get()
+ if -range_val< value+amp_val > range_val:
+ raise ValueError('Signal Output: Offset too high for '
+ 'chosen range.')
+
+ def range_valid():
+ nonlocal value
+ nonlocal number
+ toget = params['signal_output{}_autorange'.format(number+1)]
+ autorange_val = toget.get()
+ imp50_val = params['signal_output{}_imp50'.format(number+1)].get()
+ imp50_dic = {'OFF': [1.5, 0.15], 'ON': [0.75, 0.075]}
+
+ if autorange_val == "ON":
+ raise ValueError('Signal Output :'
+ ' Cannot set range as autorange is turned on.')
+
+ if value not in imp50_dic[imp50_val]:
+ raise ValueError('Signal Output: Choose a valid range:'
+ '[0.75, 0.075] if imp50 is on, [1.5, 0.15]'
+ ' otherwise.')
+
+ def ampdef_valid():
+ # check which amplitude definition you can use.
+ # dBm is only possible with 50 Ohm imp ON
+ imp50_val = params['signal_output{}_imp50'.format(number+1)].get()
+ imp50_ampdef_dict = {'ON': ['Vpk','Vrms', 'dBm'],
+ 'OFF': ['Vpk','Vrms']}
+ if value not in imp50_ampdef_dict[imp50_val]:
+ raise ValueError("Signal Output: Choose a valid amplitude "
+ "definition; ['Vpk','Vrms', 'dBm'] if imp50 is"
+ " on, ['Vpk','Vrms'] otherwise.")
+
+ dynamic_validation = {'range': range_valid,
+ 'ampdef': ampdef_valid,
+ 'amplitudes/3': amp_valid,
+ 'amplitudes/7': amp_valid,
+ 'offset': offset_valid}
+
+ def update_range_offset_amp():
+ range_val = params['signal_output{}_range'.format(number+1)].get()
+ offset_val = params['signal_output{}_offset'.format(number+1)].get()
+ amp_val = params['signal_output{}_amplitude'.format(number+1)].get()
+ if -range_val < offset_val + amp_val > range_val:
+ #The GUI would allow higher values but it would clip the signal.
+ raise ValueError('Signal Output: Amplitude and/or '
+ 'offset out of range.')
+
+ def update_offset():
+ self.parameters['signal_output{}_offset'.format(number+1)].get()
+
+ def update_amp():
+ self.parameters['signal_output{}_amplitude'.format(number+1)].get()
+
+ def update_range():
+ self.parameters['signal_output{}_autorange'.format(number+1)].get()
+
+ # parameters which will potentially change other parameters
+ changing_param = {'imp50': [update_range_offset_amp, update_range],
+ 'autorange': [update_range],
+ 'range': [update_offset, update_amp],
+ 'amplitudes/3': [update_range, update_amp],
+ 'amplitudes/7': [update_range, update_amp],
+ 'offset': [update_range]
+ }
+
+ setstr = '/{}/sigouts/{}/{}'.format(self.device, number, setting)
+
+ if setting in dynamic_validation:
+ dynamic_validation[setting]()
+
+ if mode == 0:
+ self.daq.setInt(setstr, value)
+ if mode == 1:
+ self.daq.setDouble(setstr, value)
+
+ if setting in changing_param:
+ [f() for f in changing_param[setting]]
+
+ def _sigout_getter(self, number, mode, setting):
+ """
+ Function to query the settings of signal outputs. Specific setter
+ function is needed as parameters depend on each other and need to be
+ checked and updated accordingly.
+
+ Args:
+ number (int):
+ mode (bool): Indicating whether we are asking for an int or double
+ setting (str): The module's setting to set.
+ """
+
+ querystr = '/{}/sigouts/{}/{}'.format(self.device, number, setting)
+ if mode == 0:
+ value = self.daq.getInt(querystr)
+ if mode == 1:
+ value = self.daq.getDouble(querystr)
+
+ return value
+
+ def _list_nodes(self, node):
+ """
+ Returns a list with all nodes in the sub-tree below the specified node.
+
+ Args:
+ node (str): Module of which you want to know the parameters.
+ return:
+ list of sub-nodes
+ """
+ node_list = self.daq.getList('/{}/{}/'.format(self.device, node))
+ return node_list
+
+ @staticmethod
+ def NEPBW_to_timeconstant(NEPBW, order):
+ """
+ Helper function to translate a NEP BW and a filter order
+ to a filter time constant. Meant to be used when calculating
+ sweeper sweep times.
+
+ Note: precise only to within a few percent.
+
+ Args:
+ NEPBW (float): The NEP bandwidth in Hz
+ order (int): The filter order
+
+ Returns:
+ float: The filter time constant in s.
+ """
+ const = {1: 0.249, 2: 0.124, 3: 0.093, 4: 0.078, 5: 0.068,
+ 6: 0.061, 7: 0.056, 8: 0.052}
+ tau_c = const[order]/NEPBW
+
+ return tau_c
+
+ def _get_sweep_time(self):
+ """
+ get_cmd for the sweeper_sweeptime parameter.
+
+ Note: this calculation is only an estimate and not precise to more
+ than a few percent.
+
+ Returns:
+ Union[float, None]: None if the bandwidthcontrol setting is
+ 'auto' (then all bets are off), otherwise a time in seconds.
+
+ Raises:
+ ValueError: if no signals are added to the sweep
+ """
+
+ # Possible TODO: cut down on the number of instrument
+ # queries.
+
+ if self._sweeper_signals == []:
+ raise ValueError('No signals selected! Can not find sweep time.')
+
+ mode = self.sweeper_BWmode.get()
+
+ # The effective time constant of the demodulator depends on the
+ # sweeper/bandwidthcontrol setting.
+ #
+ # If this setting is 'current', the largest current
+ # time constant of the involved demodulators is used
+ #
+ # If the setting is 'fixed', the NEP BW specified under
+ # sweep/bandwidth is used. The filter order is needed to convert
+ # the NEP BW to a time constant
+
+ demods = set([sig.split('/')[3] for sig in self._sweeper_signals])
+ rates = []
+ for demod in demods:
+ rates.append(self._getter('demods', demod, 1, 'rate'))
+ rate = min(rates)
+
+ if mode == 'current':
+ tcs = []
+ for demod in demods:
+ tcs.append(self._getter('demods', demod, 1, 'timeconstant'))
+
+ tau_c = max(tcs)
+
+ elif mode == 'fixed':
+ order = self.sweeper_order()
+ BW = self.sweeper_BW()
+
+ tau_c = self.NEPBW_to_timeconstant(BW, order)
+
+ elif mode == 'auto':
+ return None
+
+ settlingtime = max(self.sweeper_settlingtc.get()*tau_c,
+ self.sweeper_settlingtime.get())
+ averagingtime = max(self.sweeper_averaging_time.get()*tau_c*rate,
+ self.sweeper_averaging_samples.get())/rate
+
+ time_est = (settlingtime+averagingtime)*self.sweeper_samplecount.get()
+ return time_est
+
+ def _sweep_setter(self, setting, value):
+ """
+ set_cmd for all sweeper parameters. The value and setting are saved in
+ a dictionary which is read by the Sweep parameter's build_sweep method
+ and only then sent to the instrument.
+ """
+ key = '/'.join(setting.split('/')[1:])
+ self._sweepdict[key] = value
+ self.sweep_correctly_built = False
+
+ def _sweep_getter(self, setting):
+ """
+ General get_cmd for sweeper parameters
+
+ The built-in sweeper.get command returns a dictionary, but we want
+ single values.
+
+ Args:
+ setting (str): the path used by ZI to describe the setting,
+ e.g. 'sweep/settling/time'
+ """
+ # TODO: Should this look up in _sweepdict rather than query the
+ # instrument?
+ returndict = self.sweeper.get(setting) # this is a dict
+
+ # The dict may have different 'depths' depending on the parameter.
+ # The depth is encoded in the setting string (number of '/')
+ keys = setting.split('/')[1:]
+
+ while keys != []:
+ key = keys.pop(0)
+ returndict = returndict[key]
+ rawvalue = returndict
+
+ if isinstance(rawvalue, np.ndarray) and len(rawvalue) == 1:
+ value = rawvalue[0]
+ elif isinstance(rawvalue, list) and len(rawvalue) == 1:
+ value = rawvalue[0]
+ else:
+ value = rawvalue
+
+ return value
+
+ def add_signal_to_sweeper(self, demodulator, attribute):
+ """
+ Add a signal to the output of the sweeper. When the sweeper sweeps,
+ the signals added to the sweeper are returned.
+
+ Args:
+ demodulator (int): A number from 1-8 choosing the demodulator.
+ The same demodulator can be chosen several times for
+ different attributes, e.g. demod1 X, demod1 phase
+ attribute (str): The attribute to record, e.g. phase or Y
+
+ Raises:
+ ValueError: if a demodulator outside the allowed range is
+ selected
+ ValueError: if an attribute not in the list of allowed attributes
+ is selected
+ """
+
+ # TODO: implement all possibly returned attributes
+ valid_attributes = ['X', 'Y', 'R', 'phase', 'Xrms', 'Yrms', 'Rrms']
+
+ # Validation
+ if demodulator not in range(1, 9):
+ raise ValueError('Can not select demodulator' +
+ ' {}. Only '.format(demodulator) +
+ 'demodulators 1-8 are available.')
+ if attribute not in valid_attributes:
+ raise ValueError('Can not select attribute:'+
+ '{}. Only the following attributes are' +
+ ' available: ' +
+ ('{}, '*len(attributes)).format(*attributes))
+
+ # internally, we use strings very similar to the ones used by the
+ # instrument, but with the attribute added, e.g.
+ # '/dev2189/demods/0/sample/X' means X of demodulator 1.
+ signalstring = ('/' + self.device +
+ '/demods/{}/sample/{}'.format(demodulator-1,
+ attribute))
+ if signalstring not in self._sweeper_signals:
+ self._sweeper_signals.append(signalstring)
+
+ def remove_signal_from_sweeper(self, demodulator, attribute):
+ """
+ Remove a signal from the output of the sweeper. If the signal
+ has not previously been added, a warning is logged.
+
+ Args:
+ demodulator (int): A number from 1-8 choosing the demodulator.
+ The same demodulator can be chosen several times for
+ different attributes, e.g. demod1 X, demod1 phase
+ attribute (str): The attribute to record, e.g. phase or Y
+ """
+
+ signalstring = ('/' + self.device +
+ '/demods/{}/sample/{}'.format(demodulator-1,
+ attribute))
+ if signalstring not in self._sweeper_signals:
+ log.warning('Can not remove signal with {} of'.format(attribute) +
+ ' demodulator {}, since it was'.format(demodulator) +
+ ' not previously added.')
+ else:
+ self._sweeper_signals.remove(signalstring)
+
+ def print_sweeper_settings(self):
+ """
+ Pretty-print the current settings of the sweeper.
+ If Sweep.build_sweep and Sweep.get are called, the sweep described
+ here will be performed.
+ """
+ print('ACQUISITION')
+ toprint = ['sweeper_BWmode', 'sweeper_BW', 'sweeper_order',
+ 'sweeper_averaging_samples', 'sweeper_averaging_time',
+ 'sweeper_settlingtime', 'sweeper_settlingtc']
+ for paramname in toprint:
+ parameter = self.parameters[paramname]
+ print(' {}: {} ({})'.format(parameter.label, parameter.get(),
+ parameter.unit))
+
+ print('HORISONTAL')
+ toprint = ['sweeper_start', 'sweeper_stop',
+ 'sweeper_units',
+ 'sweeper_samplecount',
+ 'sweeper_param', 'sweeper_mode',
+ 'sweeper_timeout']
+ for paramname in toprint:
+ parameter = self.parameters[paramname]
+ print(' {}: {}'.format(parameter.label, parameter.get()))
+
+ print('VERTICAL')
+ count = 1
+ for signal in self._sweeper_signals:
+ (_, _, _, dm, _, attr) = signal.split('/')
+ fmt = (count, int(dm)+1, attr)
+ print(' Signal {}: Demodulator {}: {}'.format(*fmt))
+ count += 1
+
+ features = ['timeconstant', 'order', 'samplerate']
+ print('DEMODULATORS')
+ demods = []
+ for signal in self._sweeper_signals:
+ demods.append(int(signal.split('/')[3]))
+ demods = set(demods)
+ for dm in demods:
+ for feat in features:
+ parameter = self.parameters['demod{:d}_{}'.format(dm+1, feat)]
+ fmt = (dm+1, parameter.label, parameter.get(), parameter.unit)
+ print(' Demodulator {}: {}: {:.6f} ({})'.format(*fmt))
+ print('META')
+ swptime = self.sweeper_sweeptime()
+ if swptime is not None:
+ print(' Expected sweep time: {:.1f} (s)'.format(swptime))
+ else:
+ print(' Expected sweep time: N/A in auto mode')
+ print(' Sweep timeout: {} ({})'.format(self.sweeper_timeout.get(),
+ 's'))
+ ready = self.sweep_correctly_built
+ print(' Sweep built and ready to execute: {}'.format(ready))
+
+ def _scope_setter(self, scopemodule, mode, setting, value):
+ """
+ set_cmd for all scope parameters. The value and setting are saved in
+ a dictionary which is read by the Scope parameter's build_scope method
+ and only then sent to the instrument.
+
+ Args:
+ scopemodule (int): Indicates whether this is a setting of the
+ scopeModule or not. 1: it is a scopeModule setting,
+ 0: it is not.
+ mode (int): Indicates whether we are setting an int or a float.
+ 0: int, 1: float. NOTE: Ignored if scopemodule==1.
+ setting (str): The setting, e.g. 'length'.
+ value (Union[int, float, str]): The value to set.
+ """
+ # Because setpoints need to be built
+ self.scope_correctly_built = False
+
+ # Some parameters are linked to each other in specific ways
+ # Therefore, we need special actions for setting these parameters
+
+ SRtranslation = {'kHz': 1e3, 'MHz': 1e6, 'GHz': 1e9,
+ 'khz': 1e3, 'Mhz': 1e6, 'Ghz': 1e9}
+
+ def setlength(value):
+ # TODO: add validation. The GUI seems to correect this value
+ self.daq.setDouble('/{}/scopes/0/length'.format(self.device),
+ value)
+ SR_str = self.parameters['scope_samplingrate'].get()
+ (number, unit) = SR_str.split(' ')
+ SR = float(number)*SRtranslation[unit]
+ self.parameters['scope_duration']._save_val(value/SR)
+ self.daq.setInt('/{}/scopes/0/length'.format(self.device), value)
+
+ def setduration(value):
+ # TODO: validation?
+ SR_str = self.parameters['scope_samplingrate'].get()
+ (number, unit) = SR_str.split(' ')
+ SR = float(number)*SRtranslation[unit]
+ N = int(np.round(value*SR))
+ self.parameters['scope_length']._save_val(N)
+ self.parameters['scope_duration']._save_val(value)
+ self.daq.setInt('/{}/scopes/0/length'.format(self.device), N)
+
+ def setholdoffseconds(value):
+ self.parameters['scope_trig_holdoffmode'].set('s')
+ self.daq.setDouble('/{}/scopes/0/trigholdoff'.format(self.device),
+ value)
+
+ def setsamplingrate(value):
+ # When the sample rate is changed, the number of points of the trace
+ # remains unchanged and the duration changes accordingly
+ newSR_str = dict(zip(self._samplingrate_codes.values(),
+ self._samplingrate_codes.keys()))[value]
+ (number, unit) = newSR_str.split(' ')
+ newSR = float(number)*SRtranslation[unit]
+ oldSR_str = self.parameters['scope_samplingrate'].get()
+ (number, unit) = oldSR_str.split(' ')
+ oldSR = float(number)*SRtranslation[unit]
+ oldduration = self.parameters['scope_duration'].get()
+ newduration = oldduration*oldSR/newSR
+ self.parameters['scope_duration']._save_val(newduration)
+ self.daq.setInt('/{}/scopes/0/time'.format(self.device), value)
+
+ specialcases = {'length': setlength,
+ 'duration': setduration,
+ 'scope_trig_holdoffseconds': setholdoffseconds,
+ 'time': setsamplingrate}
+
+ if setting in specialcases:
+ specialcases[setting](value)
+ self.daq.sync()
+ return
+ else:
+ # We have two different parameter types: those under
+ # /scopes/0/ and those under scopeModule/
+ if scopemodule:
+ self.scope.set('scopeModule/{}'.format(setting), value)
+ elif mode == 0:
+ self.daq.setInt('/{}/scopes/0/{}'.format(self.device,
+ setting), value)
+ elif mode == 1:
+ self.daq.setDouble('/{}/scopes/0/{}'.format(self.device,
+ setting), value)
+ return
+
+ def _scope_getter(self, setting):
+ """
+ get_cmd for scopeModule parameters
+
+ """
+ # There are a few special cases
+ SRtranslation = {'kHz': 1e3, 'MHz': 1e6, 'GHz': 1e9,
+ 'khz': 1e3, 'Mhz': 1e6, 'Ghz': 1e9}
+
+ def getduration():
+ SR_str = self.parameters['scope_samplingrate'].get()
+ (number, unit) = SR_str.split(' ')
+ SR = float(number)*SRtranslation[unit]
+ N = self.parameters['scope_length'].get()
+ duration = N/SR
+ return duration
+
+ specialcases = {'duration': getduration}
+
+ if setting in specialcases:
+ value = specialcases[setting]()
+ else:
+ querystr = 'scopeModule/' + setting
+ returndict = self.scope.get(querystr)
+ # The dict may have different 'depths' depending on the parameter.
+ # The depth is encoded in the setting string (number of '/')
+ keys = setting.split('/')[1:]
+
+ while keys != []:
+ key = keys.pop(0)
+ returndict = returndict[key]
+ rawvalue = returndict
+
+ if isinstance(rawvalue, np.ndarray) and len(rawvalue) == 1:
+ value = rawvalue[0]
+ elif isinstance(rawvalue, list) and len(rawvalue) == 1:
+ value = rawvalue[0]
+ else:
+ value = rawvalue
+
+ return value
+
+ def close(self):
+ """
+ Override of the base class' close function
+ """
+ self.daq.disconnect()
+ super().close()
diff --git a/qcodes/instrument_drivers/ZI/__init__.py b/qcodes/instrument_drivers/ZI/__init__.py
new file mode 100644
index 00000000000..7b395efcbc5
--- /dev/null
+++ b/qcodes/instrument_drivers/ZI/__init__.py
@@ -0,0 +1 @@
+# emnpy __init__ file
\ No newline at end of file