Skip to content

Feature/keithley 2000 scan #85

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Jan 15, 2021
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