Skip to content

Commit

Permalink
Merge pull request microsoft#2150 from Akshita07/fixb1500measurerangebug
Browse files Browse the repository at this point in the history
Fix Keysight B1500 Measure Range Bug
  • Loading branch information
astafan8 authored Sep 18, 2020
2 parents 058b741 + 3e36359 commit e22e39b
Show file tree
Hide file tree
Showing 3 changed files with 201 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1425,7 +1425,7 @@
"metadata": {},
"source": [
"To measure do the following:\n",
" 1. Configure the measure range\n",
" 1. Configure the voltage or/and current measure ranges\n",
" 2. Enable the channel (if not yet enabled)\n",
" 3. Do the measurement\n",
" 4. (optionally) Disable the channel\n",
Expand All @@ -1439,11 +1439,13 @@
"metadata": {},
"outputs": [],
"source": [
"b1500.smu1.measure_config(measure_range=constants.IMeasRange.FIX_100uA)\n",
"b1500.smu1.i_measure_range_config(i_measure_range=constants.IMeasRange.MIN_100mA)\n",
"b1500.smu1.v_measure_range_config(v_measure_range=constants.VMeasRange.FIX_2V)\n",
"\n",
"b1500.smu1.enable_outputs()\n",
"\n",
"cur = b1500.smu1.current()\n",
"vol = b1500.smu1.voltage()\n",
"\n",
"b1500.smu1.disable_outputs()"
]
Expand Down Expand Up @@ -1669,7 +1671,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.1"
"version": "3.7.7"
},
"nbsphinx": {
"execute": "never"
Expand Down
95 changes: 90 additions & 5 deletions qcodes/instrument_drivers/Keysight/keysightb1500/KeysightB1517A.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@
from qcodes.instrument.channel import InstrumentChannel
from qcodes.instrument.group_parameter import GroupParameter, Group
from qcodes.utils.validators import Arrays
from qcodes.utils.deprecate import deprecate

from .KeysightB1500_sampling_measurement import SamplingMeasurement
from .KeysightB1500_module import B1500Module, \
parse_spot_measurement_response
from .message_builder import MessageBuilder
from . import constants
from .constants import ModuleKind, ChNr, AAD, MM, MeasurementStatus
from .constants import ModuleKind, ChNr, AAD, MM, MeasurementStatus, \
VMeasRange, IMeasRange

if TYPE_CHECKING:
from .KeysightB1500_base import KeysightB1500
Expand Down Expand Up @@ -503,7 +505,7 @@ def get_raw(self) -> ParamRawDataType:

msg = MessageBuilder().tv(
chnum=smu.channels[0],
v_range=smu._measure_config["measure_range"],
v_range=smu._measure_config["v_measure_range"],
)
response = smu.ask(msg.message)

Expand Down Expand Up @@ -543,7 +545,7 @@ def get_raw(self) -> ParamRawDataType:

msg = MessageBuilder().ti(
chnum=smu.channels[0],
i_range=smu._measure_config["measure_range"],
i_range=smu._measure_config["i_measure_range"],
)
response = smu.ask(msg.message)

Expand Down Expand Up @@ -574,7 +576,7 @@ def __init__(self, parent: 'KeysightB1500', name: Optional[str],
super().__init__(parent, name, slot_nr, **kwargs)
self.channels = (ChNr(slot_nr),)
self._measure_config: Dict[str, Optional[Any]] = {
k: None for k in ("measure_range",)}
k: None for k in ("v_measure_range", "i_measure_range",)}
self._source_config: Dict[str, Optional[Any]] = {
k: None for k in ("output_range", "compliance",
"compl_polarity", "min_compliance_range")}
Expand All @@ -589,6 +591,44 @@ def __init__(self, parent: 'KeysightB1500', name: Optional[str],
self.setup_fnc_already_run: bool = False
self.power_line_frequency: int = 50
self._average_coefficient: int = 1
self._valid_v_measure_ranges: List[VMeasRange] = [VMeasRange.AUTO,
VMeasRange.MIN_0V5,
VMeasRange.MIN_2V,
VMeasRange.MIN_5V,
VMeasRange.MIN_20V,
VMeasRange.MIN_40V,
VMeasRange.MIN_100V,
VMeasRange.FIX_0V5,
VMeasRange.FIX_2V,
VMeasRange.FIX_5V,
VMeasRange.FIX_20V,
VMeasRange.FIX_40V,
VMeasRange.FIX_100V]
self._valid_i_measure_ranges: List[IMeasRange] = [IMeasRange.AUTO,
IMeasRange.MIN_1pA,
IMeasRange.MIN_10pA,
IMeasRange.MIN_100pA,
IMeasRange.MIN_1nA,
IMeasRange.MIN_10nA,
IMeasRange.MIN_100nA,
IMeasRange.MIN_1uA,
IMeasRange.MIN_10uA,
IMeasRange.MIN_100uA,
IMeasRange.MIN_1mA,
IMeasRange.MIN_10mA,
IMeasRange.MIN_100mA,
IMeasRange.FIX_1pA,
IMeasRange.FIX_10pA,
IMeasRange.FIX_100pA,
IMeasRange.FIX_1nA,
IMeasRange.FIX_10nA,
IMeasRange.FIX_100nA,
IMeasRange.FIX_1uA,
IMeasRange.FIX_10uA,
IMeasRange.FIX_100uA,
IMeasRange.FIX_1mA,
IMeasRange.FIX_10mA,
IMeasRange.FIX_100mA]

self.add_parameter(
name="measurement_mode",
Expand Down Expand Up @@ -800,13 +840,58 @@ def source_config(
"min_compliance_range": min_compliance_range,
}

@deprecate(reason='the method confuses ranges for voltage and current '
'measurements',
alternative='v_measure_range_config or i_measure_range_config')
def measure_config(self, measure_range: constants.MeasureRange) -> None:
"""Configure measuring voltage/current
Args:
measure_range: voltage/current measurement range
"""
self._measure_config = {"measure_range": measure_range}
if measure_range in (VMeasRange.AUTO, IMeasRange.AUTO):
self.v_measure_range_config(VMeasRange.AUTO)
self.i_measure_range_config(IMeasRange.AUTO)
elif isinstance(measure_range, constants.VMeasRange):
self.v_measure_range_config(measure_range)
elif isinstance(measure_range, constants.IMeasRange):
self.i_measure_range_config(measure_range)

def v_measure_range_config(self,
v_measure_range: constants.VMeasRange) -> None:
"""Configure measuring voltage
Args:
v_measure_range: voltage measurement range
"""
if not isinstance(v_measure_range, constants.VMeasRange):
raise TypeError(f"Expected valid voltage measurement range, "
f"got {v_measure_range}.")

if v_measure_range not in self._valid_v_measure_ranges:
raise RuntimeError(f"{v_measure_range} voltage measurement "
f"range is invalid for the device. Valid "
f"ranges are {self._valid_v_measure_ranges}.")

self._measure_config["v_measure_range"] = v_measure_range

def i_measure_range_config(self,
i_measure_range: constants.IMeasRange) -> None:
"""Configure measuring current
Args:
i_measure_range: current measurement range
"""
if not isinstance(i_measure_range, constants.IMeasRange):
raise TypeError(f"Expected valid current measurement range, "
f"got {i_measure_range}.")

if i_measure_range not in self._valid_i_measure_ranges:
raise RuntimeError(f"{i_measure_range} current measurement "
f"range is invalid for the device. Valid "
f"ranges are {self._valid_i_measure_ranges}.")

self._measure_config["i_measure_range"] = i_measure_range

def timing_parameters(self,
h_bias: float,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
from qcodes.instrument_drivers.Keysight.keysightb1500.KeysightB1517A import \
B1517A
from qcodes.instrument_drivers.Keysight.keysightb1500.constants import \
VOutputRange, CompliancePolarityMode, IOutputRange, IMeasRange, MM
VOutputRange, CompliancePolarityMode, IOutputRange, IMeasRange, \
VMeasRange, MM

# pylint: disable=redefined-outer-name

Expand Down Expand Up @@ -36,7 +37,8 @@ def test_snapshot():

smu.use_high_speed_adc()
smu.source_config(output_range=VOutputRange.AUTO)
smu.measure_config(measure_range=IMeasRange.AUTO)
smu.i_measure_range_config(i_measure_range=IMeasRange.AUTO)
smu.v_measure_range_config(v_measure_range=VMeasRange.AUTO)
smu.timing_parameters(0.0, 0.123, 321)

s = smu.snapshot()
Expand All @@ -45,13 +47,112 @@ def test_snapshot():
assert 'output_range' in s['_source_config']
assert isinstance(s['_source_config']['output_range'], VOutputRange)
assert '_measure_config' in s
assert 'measure_range' in s['_measure_config']
assert isinstance(s['_measure_config']['measure_range'], IMeasRange)
assert 'v_measure_range' in s['_measure_config']
assert 'i_measure_range' in s['_measure_config']
assert isinstance(s['_measure_config']['v_measure_range'], VMeasRange)
assert isinstance(s['_measure_config']['i_measure_range'], IMeasRange)
assert '_timing_parameters' in s
assert 'number' in s['_timing_parameters']
assert isinstance(s['_timing_parameters']['number'], int)


@pytest.mark.filterwarnings("ignore:The function <measure_config>")
def test_measure_config(smu):
smu.measure_config(VMeasRange.AUTO)
s = smu.snapshot()

assert s['_measure_config']['v_measure_range'] == 0
assert s['_measure_config']['i_measure_range'] == 0

smu.measure_config(VMeasRange.FIX_0V5)
s = smu.snapshot()

assert isinstance(s['_measure_config']['v_measure_range'], VMeasRange)
assert s['_measure_config']['v_measure_range'] == -5

smu.measure_config(IMeasRange.FIX_1nA)
s = smu.snapshot()

assert isinstance(s['_measure_config']['i_measure_range'], IMeasRange)
assert s['_measure_config']['i_measure_range'] == -11

smu.measure_config(IMeasRange.AUTO)
s = smu.snapshot()

assert s['_measure_config']['v_measure_range'] == 0
assert s['_measure_config']['i_measure_range'] == 0


def test_v_measure_range_config_raises_type_error(smu):
msg = re.escape("Expected valid voltage measurement range, got 42.")

with pytest.raises(TypeError, match=msg):
smu.v_measure_range_config(v_measure_range=42)


def test_v_measure_range_config_raises_invalid_range_error(smu):
msg = re.escape("15000 voltage measurement range")
with pytest.raises(RuntimeError, match=msg):
smu.v_measure_range_config(VMeasRange.MIN_1500V)


def test_v_measure_range_config_sets_range_correctly(smu):
smu.v_measure_range_config(v_measure_range=VMeasRange.MIN_0V5)
s = smu.snapshot()

assert isinstance(s['_measure_config']['v_measure_range'], VMeasRange)
assert s['_measure_config']['v_measure_range'] == 5


def test_getting_voltage_after_calling_v_measure_range_config(smu):
mainframe = smu.parent
mainframe.ask.return_value = "NAV-000.002E-01\r"

smu.v_measure_range_config(VMeasRange.FIX_2V)

assert smu.voltage.measurement_status is None
assert pytest.approx(-0.2e-3) == smu.voltage()
assert smu.voltage.measurement_status == constants.MeasurementStatus.N

s = smu.voltage.snapshot()
assert s


def test_i_measure_range_config_raises_type_error(smu):
msg = re.escape("Expected valid current measurement range, got 99.")

with pytest.raises(TypeError, match=msg):
smu.i_measure_range_config(i_measure_range=99)


def test_i_measure_range_config_raises_invalid_range_error(smu):
msg = re.escape("-23 current measurement range")
with pytest.raises(RuntimeError, match=msg):
smu.i_measure_range_config(IMeasRange.FIX_40A)


def test_i_measure_range_config_sets_range_correctly(smu):
smu.i_measure_range_config(i_measure_range=IMeasRange.MIN_1nA)
s = smu.snapshot()

assert isinstance(s['_measure_config']['i_measure_range'], IMeasRange)
assert s['_measure_config']['i_measure_range'] == 11


def test_getting_current_after_calling_i_measure_range_config(smu):
mainframe = smu.parent
mainframe.ask.return_value = "NAI+000.005E-06\r"

smu.i_measure_range_config(IMeasRange.MIN_100mA)

assert smu.current.measurement_status is None
assert pytest.approx(0.005e-6) == smu.current()
assert smu.current.measurement_status == constants.MeasurementStatus.N

s = smu.current.snapshot()
assert s


def test_force_voltage_with_autorange(smu):
mainframe = smu.parent

Expand Down Expand Up @@ -173,7 +274,7 @@ def test_some_voltage_sourcing_and_current_measurement(smu):
mainframe = smu.parent

smu.source_config(output_range=VOutputRange.MIN_0V5, compliance=1e-9)
smu.measure_config(IMeasRange.FIX_100nA)
smu.i_measure_range_config(IMeasRange.FIX_100nA)

mainframe.ask.return_value = "NAI+000.005E-09\r"

Expand Down

0 comments on commit e22e39b

Please sign in to comment.