Skip to content

Commit

Permalink
Merge pull request #85 from ThorstenGroh/feature/Keithley_2000-SCAN
Browse files Browse the repository at this point in the history
Feature/keithley 2000 scan
  • Loading branch information
jenshnielsen authored Jan 15, 2021
2 parents f461da8 + 683cc86 commit 7ca17a3
Show file tree
Hide file tree
Showing 3 changed files with 399 additions and 19 deletions.
231 changes: 224 additions & 7 deletions docs/examples/Tektronix_Keithley_6500.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@
"# QCoDeS Example with Tektronix Keithley Digital Multimeter DMM6500"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Initialization and Connection"
]
},
{
"cell_type": "markdown",
"metadata": {},
Expand All @@ -28,24 +35,32 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"Create the instrument (in this case a DMM6500 connected via USB)"
"Create the instrument (in this case a DMM6500 connected via USB). If a scanner card is inserted, it will be detected."
]
},
{
"cell_type": "code",
"execution_count": 2,
"execution_count": 3,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Connected to: KEITHLEY INSTRUMENTS DMM6500 (serial:04438044, firmware:1.0.04b) in 0.05s\n"
"Connected to: KEITHLEY INSTRUMENTS DMM6500 (serial:04438044, firmware:1.0.04b) in 0.07s\n",
"Scanner card 2000-SCAN detected.\n"
]
}
],
"source": [
"dmm = dmm6500.Keithley_6500('dmm-1','USB0::0x05E6::0x6500::04438044::INSTR')"
"dmm = dmm6500.Keithley_6500('dmm-1','TCPIP0::192.168.1.12::inst0::INSTR')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Performing simple measurements"
]
},
{
Expand All @@ -57,23 +72,225 @@
},
{
"cell_type": "code",
"execution_count": 3,
"execution_count": 6,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"2986.62"
"5799.959"
]
},
"execution_count": 3,
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"dmm.res.measure.get()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The commands for the different measurable quantities are:\n",
"- dmm.res.measure.get(): 2 wire resistance\n",
"- dmm.fres.measure.get(): 4 wire resistance\n",
"- dmm.volt.measure.get(): DC voltage\n",
"- dmm.curr.measure.get(): DC current"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Alternatively the measurable quantities can be accessd directly via one of the following commands:\n",
"- dmm.resistance.get(): 2 wire resistance\n",
"- dmm.resistance_4w.get(): 4 wire resistance\n",
"- dmm.voltage_dc.get(): DC voltage\n",
"- dmm.current_dc.get(): DC current\n",
"For instance, the resistance via a two wire measurement can be measured with the following command:"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"5793.865"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"dmm.resistance.get()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Querying the active terminal"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The DMM6500 has a front and a rear terminal. The active terminal can only be switched via a knob on the front panel of the multimeter. The currently active terminal can be queried via:"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'FRON'"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"dmm.active_terminal.get()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"If the rear terminal is active, the same command returns:"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'REAR'"
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"dmm.active_terminal.get()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Using the 2000-SCAN scanning card"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The 2000-SCAN scanning card allows to measure up to 10 channels via the rear terminal. To measure e.g. the resistance via channel 1, use:"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"5798.519"
]
},
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"dmm.ch1.resistance.get()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Checking the active terminal"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"If the wrong terminal is active, an error is raised:"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [
{
"ename": "RuntimeError",
"evalue": "('Front terminal is active instead of rear terminal.', 'getting dmm-1_ch1_resistance')",
"output_type": "error",
"traceback": [
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[1;31mRuntimeError\u001b[0m Traceback (most recent call last)",
"\u001b[1;32m<ipython-input-11-7c3ed5015dc0>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0mdmm\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mch1\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mresistance\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mget\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
"\u001b[1;32mc:\\users\\frequency conversion\\appdata\\local\\programs\\python\\python38\\lib\\site-packages\\qcodes\\instrument\\parameter.py\u001b[0m in \u001b[0;36mget_wrapper\u001b[1;34m(*args, **kwargs)\u001b[0m\n\u001b[0;32m 583\u001b[0m \u001b[1;32mexcept\u001b[0m \u001b[0mException\u001b[0m \u001b[1;32mas\u001b[0m \u001b[0me\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 584\u001b[0m \u001b[0me\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0margs\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0me\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0margs\u001b[0m \u001b[1;33m+\u001b[0m \u001b[1;33m(\u001b[0m\u001b[1;34m'getting {}'\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mformat\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 585\u001b[1;33m \u001b[1;32mraise\u001b[0m \u001b[0me\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 586\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 587\u001b[0m \u001b[1;32mreturn\u001b[0m \u001b[0mget_wrapper\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
"\u001b[1;32mc:\\users\\frequency conversion\\appdata\\local\\programs\\python\\python38\\lib\\site-packages\\qcodes\\instrument\\parameter.py\u001b[0m in \u001b[0;36mget_wrapper\u001b[1;34m(*args, **kwargs)\u001b[0m\n\u001b[0;32m 570\u001b[0m \u001b[1;32mtry\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 571\u001b[0m \u001b[1;31m# There might be cases where a .get also has args/kwargs\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 572\u001b[1;33m \u001b[0mraw_value\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mget_function\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m*\u001b[0m\u001b[0margs\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;33m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 573\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 574\u001b[0m \u001b[0mvalue\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_from_raw_value_to_value\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mraw_value\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
"\u001b[1;32mc:\\users\\frequency conversion\\appdata\\local\\programs\\python\\python38\\lib\\site-packages\\qcodes\\utils\\command.py\u001b[0m in \u001b[0;36m__call__\u001b[1;34m(self, *args)\u001b[0m\n\u001b[0;32m 176\u001b[0m raise TypeError(\n\u001b[0;32m 177\u001b[0m 'command takes exactly {} args'.format(self.arg_count))\n\u001b[1;32m--> 178\u001b[1;33m \u001b[1;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mexec_function\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m*\u001b[0m\u001b[0margs\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
"\u001b[1;32mc:\\users\\frequency conversion\\documents\\code\\qcodes_contrib_drivers\\qcodes_contrib_drivers\\drivers\\Tektronix\\Keithley_2000_Scan.py\u001b[0m in \u001b[0;36m_measure\u001b[1;34m(self, quantity)\u001b[0m\n\u001b[0;32m 63\u001b[0m \u001b[1;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mask\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m\"READ?\"\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 64\u001b[0m \u001b[1;32melse\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 65\u001b[1;33m \u001b[1;32mraise\u001b[0m \u001b[0mRuntimeError\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m\"Front terminal is active instead of rear terminal.\"\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
"\u001b[1;31mRuntimeError\u001b[0m: ('Front terminal is active instead of rear terminal.', 'getting dmm-1_ch1_resistance')"
]
}
],
"source": [
"dmm.ch1.resistance.get()"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"ename": "RuntimeError",
"evalue": "('Rear terminal is active instead of front terminal.', 'getting dmm-1_resistance')",
"output_type": "error",
"traceback": [
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[1;31mRuntimeError\u001b[0m Traceback (most recent call last)",
"\u001b[1;32m<ipython-input-12-84d4b9528614>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0mdmm\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mresistance\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mget\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
"\u001b[1;32mc:\\users\\frequency conversion\\appdata\\local\\programs\\python\\python38\\lib\\site-packages\\qcodes\\instrument\\parameter.py\u001b[0m in \u001b[0;36mget_wrapper\u001b[1;34m(*args, **kwargs)\u001b[0m\n\u001b[0;32m 583\u001b[0m \u001b[1;32mexcept\u001b[0m \u001b[0mException\u001b[0m \u001b[1;32mas\u001b[0m \u001b[0me\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 584\u001b[0m \u001b[0me\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0margs\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0me\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0margs\u001b[0m \u001b[1;33m+\u001b[0m \u001b[1;33m(\u001b[0m\u001b[1;34m'getting {}'\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mformat\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 585\u001b[1;33m \u001b[1;32mraise\u001b[0m \u001b[0me\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 586\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 587\u001b[0m \u001b[1;32mreturn\u001b[0m \u001b[0mget_wrapper\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
"\u001b[1;32mc:\\users\\frequency conversion\\appdata\\local\\programs\\python\\python38\\lib\\site-packages\\qcodes\\instrument\\parameter.py\u001b[0m in \u001b[0;36mget_wrapper\u001b[1;34m(*args, **kwargs)\u001b[0m\n\u001b[0;32m 570\u001b[0m \u001b[1;32mtry\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 571\u001b[0m \u001b[1;31m# There might be cases where a .get also has args/kwargs\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 572\u001b[1;33m \u001b[0mraw_value\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mget_function\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m*\u001b[0m\u001b[0margs\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;33m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 573\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 574\u001b[0m \u001b[0mvalue\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_from_raw_value_to_value\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mraw_value\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
"\u001b[1;32mc:\\users\\frequency conversion\\appdata\\local\\programs\\python\\python38\\lib\\site-packages\\qcodes\\utils\\command.py\u001b[0m in \u001b[0;36m__call__\u001b[1;34m(self, *args)\u001b[0m\n\u001b[0;32m 176\u001b[0m raise TypeError(\n\u001b[0;32m 177\u001b[0m 'command takes exactly {} args'.format(self.arg_count))\n\u001b[1;32m--> 178\u001b[1;33m \u001b[1;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mexec_function\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m*\u001b[0m\u001b[0margs\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
"\u001b[1;32mc:\\users\\frequency conversion\\documents\\code\\qcodes_contrib_drivers\\qcodes_contrib_drivers\\drivers\\Tektronix\\Keithley_6500.py\u001b[0m in \u001b[0;36m_measure\u001b[1;34m(self, quantity)\u001b[0m\n\u001b[0;32m 152\u001b[0m \u001b[1;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mask\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34mf\"MEAS:{quantity}?\"\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 153\u001b[0m \u001b[1;32melse\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 154\u001b[1;33m \u001b[1;32mraise\u001b[0m \u001b[0mRuntimeError\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m\"Rear terminal is active instead of front terminal.\"\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
"\u001b[1;31mRuntimeError\u001b[0m: ('Rear terminal is active instead of front terminal.', 'getting dmm-1_resistance')"
]
}
],
"source": [
"dmm.resistance.get()"
]
}
],
"metadata": {
Expand Down
65 changes: 65 additions & 0 deletions qcodes_contrib_drivers/drivers/Tektronix/Keithley_2000_Scan.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
from functools import partial
from typing import TYPE_CHECKING

from qcodes.instrument import InstrumentChannel

if TYPE_CHECKING:
from .Keithley_6500 import Keithley_6500


class Keithley_2000_Scan_Channel(InstrumentChannel):
"""
This is the qcodes driver for a channel of the 2000-SCAN scanner card.
"""
def __init__(self, dmm: "Keithley_6500", channel: int, **kwargs) -> None:
"""
Initialize instance of scanner card Keithley 2000-SCAN
Args:
dmm: Instance of digital multimeter Keithley6500 containing the scanner card
channel: Channel number
**kwargs: Keyword arguments to pass to __init__ function of InstrumentChannel class
"""
super().__init__(dmm, f"ch{channel}", **kwargs)
self.channel = channel
self.dmm = dmm

self.add_parameter('resistance',
unit='Ohm',
label=f'Resistance CH{self.channel}',
get_parser=float,
get_cmd=partial(self._measure, 'RES'))

self.add_parameter('resistance_4w',
unit='Ohm',
label=f'Resistance (4-wire) CH{self.channel}',
get_parser=float,
get_cmd=partial(self._measure, 'FRES'))

self.add_parameter('voltage_dc',
unit='V',
label=f'DC Voltage CH{self.channel}',
get_parser=float,
get_cmd=partial(self._measure, 'VOLT'))

self.add_parameter('current_dc',
unit='A',
label=f'DC current CH{self.channel}',
get_parser=float,
get_cmd=partial(self._measure, 'CURR'))

def _measure(self, quantity: str) -> str:
"""
Measure given quantity at rear terminal of the instrument. Only perform measurement if rear terminal is
active. Send SCPI command to measure and read out given quantity.
Args:
quantity: Quantity to be measured
Returns: Measurement result
"""
if self.dmm.active_terminal.get() == 'REAR':
self.write(f"SENS:FUNC '{quantity}', (@{self.channel:d})")
self.write(f"ROUT:CLOS (@{self.channel:d})")
return self.ask("READ?")
else:
raise RuntimeError("Front terminal is active instead of rear terminal.")
Loading

0 comments on commit 7ca17a3

Please sign in to comment.