Skip to content

Commit

Permalink
Feat/channelization with small cleanup (microsoft#640)
Browse files Browse the repository at this point in the history
* Driver/Keysight DMM (microsoft#556)

* feat: Add a Keysight_34465A driver

Add a first file for a driver for the Keysight 34465A DMM and a
benchmarking notebook

* Add trigger parameters to driver

Add four trigger parameters to the driver.

* refactor: Remove 'READ?' as volt get_cmd

Make a more general get_cmd for the volt parameter. Using simply 'READ?' is wrong in many cases.

* feat: Add sampling parameters

Add four parameters for sampling.

* fix: Fix wrong volt parameter get_cmd

* WIP: Add Array parameter

* feat: Add functioning data buffer parameter

* Move notebook

* Respond to review

* feat: Add channelization

Initial commit of two new classes to allow for channelization of
instruments. This includes ChannelList, a container for channels
in an instrument, and InstrumentChannel which implements a base
class for an instance of a channel.

* feat: Add channelized Lakeshore Model336 Driver

Add a driver for the Lakeshore Model 336 temperature controller
using new channelization implementation.

* feat: Add a channelized driver for the Harvard DAC

Commits a channelized version of the Harvard DecaDAC driver
using the new channelization implementation. Note that this driver
at the present time also fixes a number of open bugs in the previous
driver, including occasional crashes (microsoft#546), incorrectly reading
DAC voltages (microsoft#563), reset (microsoft#478).

This commit also adds a number of new features to the driver
including:
- Feature and version detection
- Access to calibrated high-resolution mode of DAC
- Parameter read from memory
- Validation of writes

* fix: bug in slicing and adding code

Fix bug in slicing/adding code, not correctly passing along
channel list name.

* style: fix formatting and make codacy fixes

* style: fix more code style issues

- Removed useless methods
- Whitespace

* fix: flush is now handled correctly in instrument base class

* feat: Allow channellists to be excluded from snapshots

Allows certain channel lists to be excluded from snapshots. This is
helpful when, for example, there are multiple ways to access the same
channel.

* fix: Allow snapshots of ChannelLists

This feature is implemented by creating the idea of a submodule in the
instrument class. This is just a class which implements the Instrument
class, and may be a channel list or may just implement a logical
collection of parameters within a parent instrument.

For example, looking at the Lakeshore Model_336 driver, each channel can
either be accessed as a member of a channel list, or by using the name
of the channel:
```python3
therm = Model_336("therm", "...")
# These two accesses are equivalent
therm.A.temperature()
therm.channels[0].temperature()
```

* style: remove force arg in snapshot_base

Should think about the best way to implement this change.

* style: remove unessecary import

* fix: error in type check.

* fix: submodules should be Metadatable

Submodule objects must be Metadatables at minimum, instrument is too
restrictive for channels.

* Start adding tests for channels

* Extend tests

* Fix: ensure consistent dataset names regardless of chan[] or chanA reading

* More work on channels testing

* Make sure that instument is closed in tests

* Depend on hypothesis for test

* fix improve testing of channels

* test more:

* fix: channels pep8

* fix: ensure that channels can be extended from generator

* More tests of adding channels

* Add missing functions and parameters to channellist

* fix: function testS

* assert to unittest style

* Revert "fix: function testS"

This reverts commit ff8a316.

* Revert "Add missing functions and parameters to channellist"

This reverts commit 347c0b2.

* Remove docs of non existing attributes

* Docs: decadac quote *IDN to remove warning

* Add some channel notebooks

* fix: test_channels small tweeks

* Add support to array parameters in channels

And error clearly if you try to measure multiple multiparameters

* Mock parameters add instruments to fix names in tests

* refactor tests to reduce code duplication

* Revert "Add some channel notebooks"

This reverts commit 2dbacc0.

* feat: Add channelised QDac driver

Add a QDac driver using the new channelisation feature

* feat: Add a set method to MultiChannelInstrumentParameter

* docs: Add a driver example for the channelised Qdac

* feat: Make ChannelList take default paramclass

Make the paramclass returned by __getattr__ be customisable.

* feat: Add a channelised QDac driver

Add a channelised driver for the QDac. This requires changing the base code
for channels a bit to allow for custom MultiChannelInstrumentParameter
subclasses.

* docs: Add notebook for QDac w. channels

* style: Remove unnecessary comment

* Fix syntax/spelling errors in DAC driver

* Fix: qdac_channels remove magic number

* Doc: clarify doc string

* Fix: rename paramclass to be more specific

* Lakeshore tab to space

* pep8 lakeshore driver

* Pep8 decadac driver

* Fix warnings in qdac driver

* Annotate parameters that linter has problems with
  • Loading branch information
jenshnielsen authored and Dominik-Vogel committed Aug 7, 2017
1 parent 2dc8fe0 commit a1102f4
Show file tree
Hide file tree
Showing 12 changed files with 2,251 additions and 200 deletions.
296 changes: 296 additions & 0 deletions docs/examples/driver_examples/Qcodes example with QDac_channels.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,296 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Qcodes example with QDac_channels"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import qcodes as qc\n",
"import numpy as np\n",
"\n",
"from time import sleep\n",
"\n",
"from qcodes.instrument_drivers.QDev.QDac_channels import QDac"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"# Connect to the instrument\n",
"qdac = QDac('qdac', 'ASRL6::INSTR', update_currents=False)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Basic QDac Usage\n",
"\n",
"The QCoDeS QDac driver currently supports using\n",
" * 48 Output Channels\n",
" * 3 $\\times$ 6 temperature sensors\n",
"\n",
"Each output channel has six parameters:\n",
" * DC voltage\n",
" * DC voltage range\n",
" * Current out (read-only)\n",
" * Current out range\n",
" * slope\n",
" * sync\n",
"\n",
"The slope is the (maximal) slope in V/s that the channel can allow its voltage to change by. By default, all channels have a slope of \"Inf\". The slope can be changed dynamically, but no more than 8 channels can have a finite slope at any given time (this is due to hardware limitations)."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Current out is the current flowing through the channel this is read-only\n",
"print(qdac.ch01.i.get(), qdac.ch01.i.unit)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# The current range can be either 0 to 1 μA or 0 to 100 μA\n",
"print(qdac.ch01.irange.get())\n",
"# This is set with either 0 (0 to 1 μA) or 1 (0 to 100 μA) \n",
"qdac.ch01.irange.set(1)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# The DC voltage may directly be set and gotten\n",
"qdac.ch01.v.set(-1)\n",
"print('Channel 1 voltage: {} {}'.format(qdac.ch01.v.get(), qdac.ch01.v.unit))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Multiple channels can be addressed simultaneously via the 'channels' list\n",
"qdac.channels[0:20].v.get()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Similarly, we may set them\n",
"qdac.channels[0:2].v.set(-1)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# The maximal voltage change (in V/s) may be set for each channel\n",
"qdac.ch01.slope.set(1)\n",
"qdac.ch02.slope.set(2)\n",
"# An overview may be printed (all other channels have 'Inf' slope)\n",
"qdac.printslopes()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"# now setting channel 1 and 2 voltages will cause slow ramps to happen\n",
"qdac.ch01.v.set(0)\n",
"qdac.ch02.v.set(0)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Note that only 8 (or fewer) channels can have finite slopes at one time\n",
"# To make space for other channels, set the slope to inifite\n",
"qdac.ch01.slope('Inf')\n",
"qdac.printslopes()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"# To each channel one may assign a sync channel:\n",
"qdac.ch02.sync(2) # sync output 2 will fire a 10 ms 5 V pulse when ch02 ramps\n",
"# note that even if no visible ramp is performed (i.e. ramping from 1 V to 1 V), a pulse is still fired.\n",
"\n",
"# The sync pulse settings can be modified\n",
"qdac.ch02.sync_delay(0) # The sync pulse delay (s)\n",
"qdac.ch02.sync_duration(25e-3) # The sync pulse duration (s). Default is 10 ms."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"qdac.ch02.v.set(0)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"# syncs are unassigned by assigning sync 0\n",
"qdac.ch02.sync(0)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Attention!\n",
"\n",
"The v_range parameter is really controlling a 20 dB (amplitude factor 10) attenuator. Upon changing the vrange, the attenuator is **immediately** applied (or revoked). This will --irrespective of any slope set-- cause an instantaneous voltage change unless the channel voltage is zero. By default, all attenuators are off, and the voltage range is from -10 V to 10 V for all channels."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Here is a small example showing what to look out for\n",
"#\n",
"qdac.ch01.vrange.set(0) # Attenuation OFF (the default)\n",
"qdac.ch01.v.set(1.5)\n",
"qdac.ch01.vrange.set(1) # Attenuation ON\n",
"print(qdac.ch01.v.get()) # Returns 0.15 V\n",
"qdac.ch01.v.set(0.1)\n",
"qdac.ch01.vrange.set(0) # Attenuation OFF\n",
"print(qdac.ch01.v.get()) # returns 1 V"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Overview of channel settings\n",
"\n",
"The driver provides a method for pretty-printing the state of all channels. On startup, all channels are queried for voltage and current across them, but the current query is very slow (blame the hardware).\n",
"\n",
"The pretty-print method may or may not **update** the values for the currents, depending on the value of the `update_currents` flag. Each current reading takes some 200 ms, so updating all current values takes about 10 s."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"scrolled": false
},
"outputs": [],
"source": [
"qdac.print_overview(update_currents=True)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Temperature sensors\n",
"\n",
"Physically, the QDac consists of six boards each hosting eight channels. On three locations on each board, a temperature sensors is placed. These provide read-only parameters, named `tempX_Y` where `X` is the board number and `Y` the sensor number."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"print(qdac.temp0_0.get(), qdac.temp0_0.unit)\n",
"print(qdac.temp2_1.get(), qdac.temp0_0.unit)\n",
"print(qdac.temp5_2.get(), qdac.temp0_0.unit)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"qdac.close()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": []
}
],
"metadata": {
"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.3"
}
},
"nbformat": 4,
"nbformat_minor": 1
}
1 change: 1 addition & 0 deletions qcodes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
from qcodes.instrument.base import Instrument
from qcodes.instrument.ip import IPInstrument
from qcodes.instrument.visa import VisaInstrument
from qcodes.instrument.channel import InstrumentChannel, ChannelList

from qcodes.instrument.function import Function
from qcodes.instrument.parameter import (
Expand Down
39 changes: 38 additions & 1 deletion qcodes/instrument/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ class Instrument(Metadatable, DelegateAttributes):
functions (Dict[Function]): All the functions supported by this
instrument. Usually populated via ``add_function``
submodules (Dict[Metadatable]): All the submodules of this instrument
such as channel lists or logical groupings of parameters.
Usually populated via ``add_submodule``
"""

shared_kwargs = ()
Expand All @@ -48,6 +52,7 @@ def __init__(self, name, **kwargs):
super().__init__(**kwargs)
self.parameters = {}
self.functions = {}
self.submodules = {}

self.name = str(name)

Expand Down Expand Up @@ -306,6 +311,36 @@ def add_function(self, name, **kwargs):
func = Function(name=name, instrument=self, **kwargs)
self.functions[name] = func

def add_submodule(self, name, submodule):
"""
Bind one submodule to this instrument.
Instrument subclasses can call this repeatedly in their ``__init__``
method for every submodule of the instrument.
Submodules can effectively be considered as instruments within the main
instrument, and should at minimum be snapshottable. For example, they can
be used to either store logical groupings of parameters, which may or may
not be repeated, or channel lists.
Args:
name (str): how the submodule will be stored within ``instrument.submodules``
and also how it can be addressed.
submodule (Metadatable): The submodule to be stored.
Raises:
KeyError: if this instrument already contains a submodule with this
name.
TypeError: if the submodule that we are trying to add is not an instance
of an Metadatable object.
"""
if name in self.submodules:
raise KeyError('Duplicate submodule name {}'.format(name))
if not isinstance(submodule, Metadatable):
raise TypeError('Submodules must be metadatable.')
self.submodules[name] = submodule

def snapshot_base(self, update=False):
"""
State of the instrument as a JSON-compatible dict.
Expand All @@ -321,6 +356,8 @@ def snapshot_base(self, update=False):
for name, param in self.parameters.items()),
'functions': dict((name, func.snapshot(update=update))
for name, func in self.functions.items()),
'submodules': dict((name, subm.snapshot(update=update))
for name, subm in self.submodules.items()),
'__class__': full_class(self),
}
for attr in set(self._meta_attrs):
Expand Down Expand Up @@ -459,7 +496,7 @@ def ask_raw(self, cmd):
# etc... #
#

delegate_attr_dicts = ['parameters', 'functions']
delegate_attr_dicts = ['parameters', 'functions', 'submodules']

def __getitem__(self, key):
"""Delegate instrument['name'] to parameter or function 'name'."""
Expand Down
Loading

0 comments on commit a1102f4

Please sign in to comment.