From 54b68ce4601ef6af2a9609df00fbe9592dc99514 Mon Sep 17 00:00:00 2001 From: Neil Dick Date: Tue, 22 Dec 2020 09:56:21 +1100 Subject: [PATCH 01/81] feat: Add skeleton for Keysight N9030B Add barebones skeleton for the Keysight N9030B. Functionality to follow. --- qcodes/instrument_drivers/Keysight/N9030B.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 qcodes/instrument_drivers/Keysight/N9030B.py diff --git a/qcodes/instrument_drivers/Keysight/N9030B.py b/qcodes/instrument_drivers/Keysight/N9030B.py new file mode 100644 index 000000000000..4cec325dde21 --- /dev/null +++ b/qcodes/instrument_drivers/Keysight/N9030B.py @@ -0,0 +1,12 @@ +from qcodes import VisaInstrument + + +class N9030B(VisaInstrument): + """ + Driver for Keysight N9030B PXA signal analyzer. + """ + + def __init__(self, name, address, **kwargs): + super().__init__(name, address, terminator='\n', **kwargs) + + self.connect_message() From 3a663f9162bdc983d7da44ad01ed9519263c4026 Mon Sep 17 00:00:00 2001 From: Neil Dick Date: Tue, 22 Dec 2020 10:35:49 +1100 Subject: [PATCH 02/81] feat: N9030B Add missing type annotations Add the missing type annotations for the main class. --- qcodes/instrument_drivers/Keysight/N9030B.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qcodes/instrument_drivers/Keysight/N9030B.py b/qcodes/instrument_drivers/Keysight/N9030B.py index 4cec325dde21..0ff049456625 100644 --- a/qcodes/instrument_drivers/Keysight/N9030B.py +++ b/qcodes/instrument_drivers/Keysight/N9030B.py @@ -6,7 +6,7 @@ class N9030B(VisaInstrument): Driver for Keysight N9030B PXA signal analyzer. """ - def __init__(self, name, address, **kwargs): + def __init__(self, name: str, address: str, **kwargs: Any) -> None: super().__init__(name, address, terminator='\n', **kwargs) self.connect_message() From bb95c91b7ac6016ba0c04032169fce0ba4ec5bc6 Mon Sep 17 00:00:00 2001 From: Neil Dick Date: Tue, 22 Dec 2020 10:36:22 +1100 Subject: [PATCH 03/81] feat: N9030B Add trace class and initial params Add a few instrument parameters that depend on the ArrayParameter subclass. --- qcodes/instrument_drivers/Keysight/N9030B.py | 54 +++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/qcodes/instrument_drivers/Keysight/N9030B.py b/qcodes/instrument_drivers/Keysight/N9030B.py index 0ff049456625..c15415541568 100644 --- a/qcodes/instrument_drivers/Keysight/N9030B.py +++ b/qcodes/instrument_drivers/Keysight/N9030B.py @@ -1,4 +1,7 @@ -from qcodes import VisaInstrument +from qcodes import VisaInstrument, Instrument +from qcodes.instrument.parameter import ArrayParameter +import numpy as np +from typing import Any class N9030B(VisaInstrument): @@ -9,4 +12,53 @@ class N9030B(VisaInstrument): def __init__(self, name: str, address: str, **kwargs: Any) -> None: super().__init__(name, address, terminator='\n', **kwargs) + self.add_parameter(name='gain', + get_cmd='READ:CORR:GAIN?', + parameter_class=Trace, + unit='dB', + label='Gain' + ) + + self.add_parameter(name='phot', + get_cmd='READ:CORR:PHOT?', + parameter_class=Trace, + unit='dB', + label='P-HOT' + ) + + self.add_parameter(name='pcold', + get_cmd='READ:CORR:PCOL?', + parameter_class=Trace, + unit='dB', + label='P-COLD' + ) + self.connect_message() + + +class Trace(ArrayParameter): + + def __init__(self, name: str, instrument: Instrument, get_cmd, unit: str, + label: str) -> None: + self._cmd = get_cmd + npts = instrument.npts() + super().__init__(name, (npts,)) + self._instrument = instrument + self.shape = (npts,) + self.unit = unit + self.label = label + self.setpoint_units = ('Hz',) + self.setpoint_names = ('Frequency',) + + self.set_sweep(self._instrument.fstart(), + self._instrument.fstop(), npts) + + def set_sweep(self, start: float, stop: float, npts: int) -> None: + f = tuple(np.linspace(float(start), float(stop), num=npts)) + self.setpoints = (f,) + self.shape = (npts,) + + def get(self) -> np.ndarray: + array_data = np.array(self._instrument.ask( + self._cmd).split(','), dtype='f') + return array_data From 439f75ce2a255e8cc921463043ba13694a7a8f02 Mon Sep 17 00:00:00 2001 From: Akshita07 Date: Mon, 28 Dec 2020 17:19:46 +0100 Subject: [PATCH 04/81] Update driver skeleton --- qcodes/instrument_drivers/Keysight/N9030B.py | 238 +++++++++++++++---- 1 file changed, 189 insertions(+), 49 deletions(-) diff --git a/qcodes/instrument_drivers/Keysight/N9030B.py b/qcodes/instrument_drivers/Keysight/N9030B.py index c15415541568..20ca32fa4129 100644 --- a/qcodes/instrument_drivers/Keysight/N9030B.py +++ b/qcodes/instrument_drivers/Keysight/N9030B.py @@ -1,7 +1,24 @@ -from qcodes import VisaInstrument, Instrument -from qcodes.instrument.parameter import ArrayParameter import numpy as np -from typing import Any +from typing import Any, Tuple + +from qcodes import VisaInstrument, Parameter, ParameterWithSetpoints +from qcodes.instrument.parameter import ParamRawDataType +from qcodes.utils.validators import Enum, Numbers, Arrays +from qcodes.utils.helpers import create_on_off_val_mapping + + +class FrequencyAxis(Parameter): + + def get_raw(self) -> ParamRawDataType: + npts = self.root_instrument.npts() + start = self.root_instrument.start() + stop = self.root_instrument.stop() + + return np.linspace(start, stop, npts) + + +class Trace(ParameterWithSetpoints): + pass class N9030B(VisaInstrument): @@ -12,53 +29,176 @@ class N9030B(VisaInstrument): def __init__(self, name: str, address: str, **kwargs: Any) -> None: super().__init__(name, address, terminator='\n', **kwargs) - self.add_parameter(name='gain', - get_cmd='READ:CORR:GAIN?', - parameter_class=Trace, - unit='dB', - label='Gain' - ) - - self.add_parameter(name='phot', - get_cmd='READ:CORR:PHOT?', - parameter_class=Trace, - unit='dB', - label='P-HOT' - ) - - self.add_parameter(name='pcold', - get_cmd='READ:CORR:PCOL?', - parameter_class=Trace, - unit='dB', - label='P-COLD' - ) + self._min_freq: float = 2 + self._min_freq: float = 50e9 + + self.add_parameter( + name="mode", + get_cmd=":INSTrument:SELect?", + set_cmd=":INSTrument:SELect {}", + vals=Enum(*self._available_modes()) + ) + + self.add_parameter( + name="measurement", + get_cmd=":CONFigure?", + set_cmd=":CONFigure:{}", + val_mapping={"Swept SA": "SANalyzer"} + ) + + self.add_parameter( + name="start", + get_cmd=":SENSe:FREQuency:STARt?", + set_cmd=self._set_start, + get_parser=float, + vals=Numbers(self._min_freq, self._max_freq - 10) + ) + + self.add_parameter( + name="stop", + get_cmd=":SENSe:FREQuency:STOP?", + set_cmd=self._set_stop, + get_parser=float, + vals=Numbers(self._min_freq + 10, self._max_freq) + ) + + self.add_parameter( + name="center", + get_cmd=":SENSe:FREQuency:CENTer?", + set_cmd=self._set_center, + get_parser=float, + vals=Numbers(self._min_freq + 5, self._max_freq - 5) + ) + + self.add_parameter( + name="span", + get_cmd=":SENSe:FREQuency:SPAN?", + set_cmd=self._set_span, + get_parser=float, + vals=Numbers(10, self._max_freq - self._min_freq), + ) + + self.add_parameter( + name="npts", + get_cmd=":SENSe:SWEep:POINts?", + set_cmd=self._set_npts, + get_parser=int, + ) + + self.add_parameter( + name="sweep_time", + label="Sweep time", + get_cmd=":SENSe:SWEep:TIME?", + get_parser=float, + unit="s", + ) + + self.add_parameter( + name="auto_sweep_time_enabled", + initial_value=False, + get_cmd=":SENSe:SWEep:TIME:AUTO?", + set_cmd=self._enable_auto_sweep_time, + val_mapping=create_on_off_val_mapping(on_val="ON", off_val="OFF") + ) + + self.add_parameter( + name="auto_sweep_type_enabled", + initial_value=False, + get_cmd=":SENSe:SWEep:TYPE:AUTO?", + set_cmd=self._enable_auto_sweep_type, + val_mapping=create_on_off_val_mapping(on_val="ON", off_val="OFF") + ) + + self.add_parameter( + name="sweep_type", + get_cmd=":SENSe:SWEep:TYPE?", + set_cmd=self._set_sweep_type, + val_mapping={ + "fft": "FFT", + "sweep": "SWE", + } + ) + + self.add_parameter( + name='freq_axis', + label='Frequency', + unit='Hz', + vals=Arrays(shape=(self.npts,)), + parameter_class=FrequencyAxis + ) + + self.add_parameter( + name='trace', + vals=Arrays(shape=(self.npts,)), + setpoints=(self.freq_axis,), + parameter_class=Trace + ) + + self.add_function("reset", call_cmd="*RST") + self.add_function("abort", call_cmd=":ABORt") + self.add_function("autotune", call_cmd=":SENS:FREQuency:TUNE:IMMediate") + self.add_function("cont_meas_on", call_cmd=":INITiate:CONTinuous ON") + self.add_function("cont_meas_off", call_cmd=":INITiate:CONTinuous OFF") self.connect_message() + def _available_modes(self) -> Tuple[str, ...]: + available_modes = self.ask(":INSTrument:CATalog?") + return tuple(available_modes.split(',')) + + def _set_start(self, val: float) -> None: + stop = self.stop() + if val >= stop: + raise ValueError(f"Start frequency must be smaller than stop " + f"frequency. Provided start freq is: {val} Hz and " + f"set stop freq is: {stop} Hz") + + self.write(f":SENSe:FREQuency:STARt {val}") + + start = self.start() + if abs(val - start) >= 1: + self.log.warning( + f"Could not set start to {val} setting it to {start}" + ) + + def _set_stop(self, val: float) -> None: + start = self.start() + if val <= start: + raise ValueError(f"Stop frequency must be larger than start " + f"frequency. Provided stop freq is: {val} Hz and " + f"set start freq is: {start} Hz") + + self.write(f":SENSe:FREQuency:STOP {val}") + + stop = self.stop() + if abs(val - stop) >= 1: + self.log.warning( + f"Could not set stop to {val} setting it to {stop}" + ) + + def _set_center(self, val: float) -> None: + self.write(f":SENSe:FREQuency:CENTer {val}") + self.update_trace() + + def _set_span(self, val: float) -> None: + self.write(f":SENSe:FREQuency:SPAN {val}") + self.update_trace() + + def _set_npts(self, val: int) -> None: + self.write(f":SENSe:SWEep:POINts {val}") + self.update_trace() + + def _enable_auto_sweep_time(self, val: str) -> None: + self.write(f":SENSe:SWEep:TIME:AUTO {val}") + + def _enable_auto_sweep_type(self, val: str) -> None: + self.write(f":SENSe:SWEep:TYPE:AUTO {val}") + + def _set_sweep_type(self, val: str) -> None: + self.write(f":SENSe:SWEep:TYPE {val}") + + def _get_data(self) -> ParamRawDataType: + pass -class Trace(ArrayParameter): - - def __init__(self, name: str, instrument: Instrument, get_cmd, unit: str, - label: str) -> None: - self._cmd = get_cmd - npts = instrument.npts() - super().__init__(name, (npts,)) - self._instrument = instrument - self.shape = (npts,) - self.unit = unit - self.label = label - self.setpoint_units = ('Hz',) - self.setpoint_names = ('Frequency',) - - self.set_sweep(self._instrument.fstart(), - self._instrument.fstop(), npts) - - def set_sweep(self, start: float, stop: float, npts: int) -> None: - f = tuple(np.linspace(float(start), float(stop), num=npts)) - self.setpoints = (f,) - self.shape = (npts,) - - def get(self) -> np.ndarray: - array_data = np.array(self._instrument.ask( - self._cmd).split(','), dtype='f') - return array_data + def update_trace(self) -> None: + pass From 62e3131152fdc1e48db162cb86194e1a4e7157bf Mon Sep 17 00:00:00 2001 From: Akshita07 Date: Tue, 29 Dec 2020 10:11:41 +0100 Subject: [PATCH 05/81] Update freq axis parameter --- qcodes/instrument_drivers/Keysight/N9030B.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/qcodes/instrument_drivers/Keysight/N9030B.py b/qcodes/instrument_drivers/Keysight/N9030B.py index 20ca32fa4129..7f68df34ba61 100644 --- a/qcodes/instrument_drivers/Keysight/N9030B.py +++ b/qcodes/instrument_drivers/Keysight/N9030B.py @@ -9,12 +9,15 @@ class FrequencyAxis(Parameter): - def get_raw(self) -> ParamRawDataType: - npts = self.root_instrument.npts() - start = self.root_instrument.start() - stop = self.root_instrument.stop() + def __init__(self, start: float, stop: float, npts: int, *args: Any, + **kwargs: Any) -> None: + super().__init__(*args, **kwargs) + self._start: float = start + self._stop: float = stop + self._npts: int = npts - return np.linspace(start, stop, npts) + def get_raw(self) -> Arrays: + return np.linspace(self._start, self._stop, self._npts) class Trace(ParameterWithSetpoints): @@ -30,7 +33,7 @@ def __init__(self, name: str, address: str, **kwargs: Any) -> None: super().__init__(name, address, terminator='\n', **kwargs) self._min_freq: float = 2 - self._min_freq: float = 50e9 + self._max_freq: float = 50e9 self.add_parameter( name="mode", @@ -123,7 +126,10 @@ def __init__(self, name: str, address: str, **kwargs: Any) -> None: name='freq_axis', label='Frequency', unit='Hz', - vals=Arrays(shape=(self.npts,)), + start=self.start, + stop=self.stop, + npts=self.npts, + vals=Arrays(shape=(self.npts.get_latest,)), parameter_class=FrequencyAxis ) From d1c59e0fe3da1389870d3e65e22e88535dcb6dfb Mon Sep 17 00:00:00 2001 From: Akshita07 Date: Tue, 29 Dec 2020 10:24:34 +0100 Subject: [PATCH 06/81] Add validator for npts --- qcodes/instrument_drivers/Keysight/N9030B.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/qcodes/instrument_drivers/Keysight/N9030B.py b/qcodes/instrument_drivers/Keysight/N9030B.py index 7f68df34ba61..007fd34c38d1 100644 --- a/qcodes/instrument_drivers/Keysight/N9030B.py +++ b/qcodes/instrument_drivers/Keysight/N9030B.py @@ -3,7 +3,7 @@ from qcodes import VisaInstrument, Parameter, ParameterWithSetpoints from qcodes.instrument.parameter import ParamRawDataType -from qcodes.utils.validators import Enum, Numbers, Arrays +from qcodes.utils.validators import Enum, Numbers, Arrays, Ints from qcodes.utils.helpers import create_on_off_val_mapping @@ -86,6 +86,7 @@ def __init__(self, name: str, address: str, **kwargs: Any) -> None: get_cmd=":SENSe:SWEep:POINts?", set_cmd=self._set_npts, get_parser=int, + vals=Ints(1, 20001) ) self.add_parameter( From b1407991276e81e19b28b16d8c899d0cfd87ff76 Mon Sep 17 00:00:00 2001 From: Akshita07 Date: Tue, 29 Dec 2020 12:13:37 +0100 Subject: [PATCH 07/81] Add trace parameter and measurement related methods --- qcodes/instrument_drivers/Keysight/N9030B.py | 59 ++++++++++++++++---- 1 file changed, 49 insertions(+), 10 deletions(-) diff --git a/qcodes/instrument_drivers/Keysight/N9030B.py b/qcodes/instrument_drivers/Keysight/N9030B.py index 007fd34c38d1..e322b954859e 100644 --- a/qcodes/instrument_drivers/Keysight/N9030B.py +++ b/qcodes/instrument_drivers/Keysight/N9030B.py @@ -16,12 +16,17 @@ def __init__(self, start: float, stop: float, npts: int, *args: Any, self._stop: float = stop self._npts: int = npts - def get_raw(self) -> Arrays: + def get_raw(self) -> ParamRawDataType: return np.linspace(self._start, self._stop, self._npts) class Trace(ParameterWithSetpoints): - pass + + def __init__(self, *args: Any, **kwargs: Any) -> None: + super().__init__(*args, **kwargs) + + def get_raw(self) -> ParamRawDataType: + return self._get_data() class N9030B(VisaInstrument): @@ -34,6 +39,7 @@ def __init__(self, name: str, address: str, **kwargs: Any) -> None: self._min_freq: float = 2 self._max_freq: float = 50e9 + self._additional_wait: float = 1 self.add_parameter( name="mode", @@ -124,9 +130,21 @@ def __init__(self, name: str, address: str, **kwargs: Any) -> None: ) self.add_parameter( - name='freq_axis', - label='Frequency', - unit='Hz', + name="format", + get_cmg=":FORMat:TRACe:DATA?", + set_cmd=":FORMat:TRACe:DATA {}", + val_mapping={ + "ascii": "ASCii", + "int32": "INTeger,32", + "real32": "REAL,32", + "real64": "REAL,64" + } + ) + + self.add_parameter( + name="freq_axis", + label="Frequency", + unit="Hz", start=self.start, stop=self.stop, npts=self.npts, @@ -135,8 +153,9 @@ def __init__(self, name: str, address: str, **kwargs: Any) -> None: ) self.add_parameter( - name='trace', - vals=Arrays(shape=(self.npts,)), + name="trace", + unit="dB", + vals=Arrays(shape=(self.npts.get_latest,)), setpoints=(self.freq_axis,), parameter_class=Trace ) @@ -193,7 +212,6 @@ def _set_span(self, val: float) -> None: def _set_npts(self, val: int) -> None: self.write(f":SENSe:SWEep:POINts {val}") - self.update_trace() def _enable_auto_sweep_time(self, val: str) -> None: self.write(f":SENSe:SWEep:TIME:AUTO {val}") @@ -205,7 +223,28 @@ def _set_sweep_type(self, val: str) -> None: self.write(f":SENSe:SWEep:TYPE {val}") def _get_data(self) -> ParamRawDataType: - pass + + # trace number + n: int = 2 + with self.status.set_to(1): + self.cont_meas_off() + try: + timeout = self.sweep_time() + self._additional_wait + with self.timeout.set_to(timeout): + data_str = self.ask(f":READ:" + f"{self.measurement}{n}?") + data = np.array(data_str.rstrip()).astype("float64") + finally: + self.cont_meas_on() + return data def update_trace(self) -> None: - pass + self.start() + self.stop() + + def setup_sweep(self, start: float, stop: float, npts: int) -> None: + self.mode("SA") + self.measurement("Swept SA") + self.start(start) + self.stop(stop) + self.npts(npts) From 0af867a6d3ed7ae7fd3416d3b1a9e71ecc82e76c Mon Sep 17 00:00:00 2001 From: Akshita07 Date: Tue, 29 Dec 2020 13:22:51 +0100 Subject: [PATCH 08/81] Update trace param class --- qcodes/instrument_drivers/Keysight/N9030B.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/qcodes/instrument_drivers/Keysight/N9030B.py b/qcodes/instrument_drivers/Keysight/N9030B.py index e322b954859e..c6c5448a32a5 100644 --- a/qcodes/instrument_drivers/Keysight/N9030B.py +++ b/qcodes/instrument_drivers/Keysight/N9030B.py @@ -22,11 +22,8 @@ def get_raw(self) -> ParamRawDataType: class Trace(ParameterWithSetpoints): - def __init__(self, *args: Any, **kwargs: Any) -> None: - super().__init__(*args, **kwargs) - def get_raw(self) -> ParamRawDataType: - return self._get_data() + return self.instrument._get_data() class N9030B(VisaInstrument): From 99037034916f9552fdf7a14cdbed1b55e10ddb4e Mon Sep 17 00:00:00 2001 From: Akshita07 Date: Tue, 12 Jan 2021 17:10:11 +0100 Subject: [PATCH 09/81] Replace add_functions with methods and parameter --- qcodes/instrument_drivers/Keysight/N9030B.py | 37 ++++++++++++++++---- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/qcodes/instrument_drivers/Keysight/N9030B.py b/qcodes/instrument_drivers/Keysight/N9030B.py index c6c5448a32a5..a5ed9f3c75b6 100644 --- a/qcodes/instrument_drivers/Keysight/N9030B.py +++ b/qcodes/instrument_drivers/Keysight/N9030B.py @@ -157,11 +157,13 @@ def __init__(self, name: str, address: str, **kwargs: Any) -> None: parameter_class=Trace ) - self.add_function("reset", call_cmd="*RST") - self.add_function("abort", call_cmd=":ABORt") - self.add_function("autotune", call_cmd=":SENS:FREQuency:TUNE:IMMediate") - self.add_function("cont_meas_on", call_cmd=":INITiate:CONTinuous ON") - self.add_function("cont_meas_off", call_cmd=":INITiate:CONTinuous OFF") + self.add_parameter( + name="cont_meas", + initial_value=False, + get_cmd=":INITiate:CONTinuous?", + set_cmd=self._enable_cont_meas, + val_mapping=create_on_off_val_mapping(on_val="ON", off_val="OFF") + ) self.connect_message() @@ -219,12 +221,15 @@ def _enable_auto_sweep_type(self, val: str) -> None: def _set_sweep_type(self, val: str) -> None: self.write(f":SENSe:SWEep:TYPE {val}") + def _enable_cont_meas(self, val: str) -> None: + self.write(f":INITiate:CONTinuous {val}") + def _get_data(self) -> ParamRawDataType: # trace number n: int = 2 with self.status.set_to(1): - self.cont_meas_off() + self.cont_meas("OFF") try: timeout = self.sweep_time() + self._additional_wait with self.timeout.set_to(timeout): @@ -232,7 +237,7 @@ def _get_data(self) -> ParamRawDataType: f"{self.measurement}{n}?") data = np.array(data_str.rstrip()).astype("float64") finally: - self.cont_meas_on() + self.cont_meas("ON") return data def update_trace(self) -> None: @@ -245,3 +250,21 @@ def setup_sweep(self, start: float, stop: float, npts: int) -> None: self.start(start) self.stop(stop) self.npts(npts) + + def reset(self) -> None: + """ + Reset the instrument by sending the RST command + """ + self.write("*RST") + + def abort(self) -> None: + """ + Aborts the measurement + """ + self.write(":ABORt") + + def autotune(self) -> None: + """ + Autotunes frequency + """ + self.write(":SENS:FREQuency:TUNE:IMMediate") From 4b5244025825bf6dbfdd3d7dc1fb9deebb5a64d2 Mon Sep 17 00:00:00 2001 From: Akshita07 Date: Wed, 13 Jan 2021 10:06:56 +0100 Subject: [PATCH 10/81] Add init method to trace class --- qcodes/instrument_drivers/Keysight/N9030B.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/qcodes/instrument_drivers/Keysight/N9030B.py b/qcodes/instrument_drivers/Keysight/N9030B.py index a5ed9f3c75b6..14cd8c19380b 100644 --- a/qcodes/instrument_drivers/Keysight/N9030B.py +++ b/qcodes/instrument_drivers/Keysight/N9030B.py @@ -22,6 +22,10 @@ def get_raw(self) -> ParamRawDataType: class Trace(ParameterWithSetpoints): + def __init__(self, *args: Any, **kwargs: Any) -> None: + super().__init__(*args, **kwargs) + self.instrument: "N9030B" + def get_raw(self) -> ParamRawDataType: return self.instrument._get_data() From e85e384bda49dbfd23c98fee3e6fd648395904b5 Mon Sep 17 00:00:00 2001 From: Akshita07 Date: Thu, 14 Jan 2021 11:02:03 +0100 Subject: [PATCH 11/81] Reaggance and add separate class for SA mode --- qcodes/instrument_drivers/Keysight/N9030B.py | 207 +++++++++++++------ 1 file changed, 145 insertions(+), 62 deletions(-) diff --git a/qcodes/instrument_drivers/Keysight/N9030B.py b/qcodes/instrument_drivers/Keysight/N9030B.py index 14cd8c19380b..19f6f0923cd3 100644 --- a/qcodes/instrument_drivers/Keysight/N9030B.py +++ b/qcodes/instrument_drivers/Keysight/N9030B.py @@ -22,12 +22,15 @@ def get_raw(self) -> ParamRawDataType: class Trace(ParameterWithSetpoints): - def __init__(self, *args: Any, **kwargs: Any) -> None: + def __init__(self, number: int, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) - self.instrument: "N9030B" + self.instrument: "SpectrumAnalyzer" + self.root_instrument: "N9030B" + + self.n = number def get_raw(self) -> ParamRawDataType: - return self.instrument._get_data() + return self.instrument._get_data(trace_num=self.n) class N9030B(VisaInstrument): @@ -38,8 +41,8 @@ class N9030B(VisaInstrument): def __init__(self, name: str, address: str, **kwargs: Any) -> None: super().__init__(name, address, terminator='\n', **kwargs) - self._min_freq: float = 2 - self._max_freq: float = 50e9 + self._min_freq: float + self._max_freq: float self._additional_wait: float = 1 self.add_parameter( @@ -49,11 +52,66 @@ def __init__(self, name: str, address: str, **kwargs: Any) -> None: vals=Enum(*self._available_modes()) ) + self.add_parameter( + name="cont_meas", + initial_value=False, + get_cmd=":INITiate:CONTinuous?", + set_cmd=self._enable_cont_meas, + val_mapping=create_on_off_val_mapping(on_val="ON", off_val="OFF") + ) + + self.connect_message() + + def _available_modes(self) -> Tuple[str, ...]: + available_modes = self.ask(":INSTrument:CATalog?") + return tuple(available_modes.split(',')) + + def _available_meas(self) -> Tuple[str, ...]: + """ + Gives available measurement with a given mode for the instrument + """ + available_meas = self.ask(":CONFigure:CATalog?") + return tuple(available_meas.split(',')) + + def _enable_cont_meas(self, val: str) -> None: + self.write(f":INITiate:CONTinuous {val}") + + def reset(self) -> None: + """ + Reset the instrument by sending the RST command + """ + self.write("*RST") + + def abort(self) -> None: + """ + Aborts the measurement + """ + self.write(":ABORt") + + +class SpectrumAnalyzer(N9030B): + """ + Spectrum Analyzer Mode for Kyesight N9030B instrument. + """ + def __init__(self, name: str, *arg, **kwargs): + super().__init__(name, *arg, **kwargs) + + if "SA" in self._available_modes(): + self.mode("SA") + else: + raise RuntimeError("Spectrum Analyzer Mode is not available on " + "your Keysight N9030B instrument.") + + self._min_freq = 2 + self._max_freq = 50e9 + self.add_parameter( name="measurement", get_cmd=":CONFigure?", set_cmd=":CONFigure:{}", - val_mapping={"Swept SA": "SANalyzer"} + vals=Enum(*self._available_meas()), + docstring="Sets measurement type from among the available " + "measurement types." ) self.add_parameter( @@ -61,7 +119,8 @@ def __init__(self, name: str, address: str, **kwargs: Any) -> None: get_cmd=":SENSe:FREQuency:STARt?", set_cmd=self._set_start, get_parser=float, - vals=Numbers(self._min_freq, self._max_freq - 10) + vals=Numbers(self._min_freq, self._max_freq - 10), + docstring="start frequency for the sweep" ) self.add_parameter( @@ -69,7 +128,8 @@ def __init__(self, name: str, address: str, **kwargs: Any) -> None: get_cmd=":SENSe:FREQuency:STOP?", set_cmd=self._set_stop, get_parser=float, - vals=Numbers(self._min_freq + 10, self._max_freq) + vals=Numbers(self._min_freq + 10, self._max_freq), + docstring="stop frequency for the sweep" ) self.add_parameter( @@ -77,7 +137,8 @@ def __init__(self, name: str, address: str, **kwargs: Any) -> None: get_cmd=":SENSe:FREQuency:CENTer?", set_cmd=self._set_center, get_parser=float, - vals=Numbers(self._min_freq + 5, self._max_freq - 5) + vals=Numbers(self._min_freq + 5, self._max_freq - 5), + docstring="Sets and gets center frequency" ) self.add_parameter( @@ -86,6 +147,7 @@ def __init__(self, name: str, address: str, **kwargs: Any) -> None: set_cmd=self._set_span, get_parser=float, vals=Numbers(10, self._max_freq - self._min_freq), + docstring="Changes span of frequency" ) self.add_parameter( @@ -93,7 +155,8 @@ def __init__(self, name: str, address: str, **kwargs: Any) -> None: get_cmd=":SENSe:SWEep:POINts?", set_cmd=self._set_npts, get_parser=int, - vals=Ints(1, 20001) + vals=Ints(1, 20001), + docstring="Number of points for the sweep" ) self.add_parameter( @@ -102,6 +165,7 @@ def __init__(self, name: str, address: str, **kwargs: Any) -> None: get_cmd=":SENSe:SWEep:TIME?", get_parser=float, unit="s", + docstring="gets sweep time" ) self.add_parameter( @@ -109,7 +173,8 @@ def __init__(self, name: str, address: str, **kwargs: Any) -> None: initial_value=False, get_cmd=":SENSe:SWEep:TIME:AUTO?", set_cmd=self._enable_auto_sweep_time, - val_mapping=create_on_off_val_mapping(on_val="ON", off_val="OFF") + val_mapping=create_on_off_val_mapping(on_val="ON", off_val="OFF"), + docstring="enables auto sweep time" ) self.add_parameter( @@ -117,7 +182,8 @@ def __init__(self, name: str, address: str, **kwargs: Any) -> None: initial_value=False, get_cmd=":SENSe:SWEep:TYPE:AUTO?", set_cmd=self._enable_auto_sweep_type, - val_mapping=create_on_off_val_mapping(on_val="ON", off_val="OFF") + val_mapping=create_on_off_val_mapping(on_val="ON", off_val="OFF"), + docstring="enables auto sweep type" ) self.add_parameter( @@ -127,19 +193,22 @@ def __init__(self, name: str, address: str, **kwargs: Any) -> None: val_mapping={ "fft": "FFT", "sweep": "SWE", - } + }, + docstring="Sets up sweep type. Possible options are 'fft' and " + "'sweep'." ) self.add_parameter( name="format", - get_cmg=":FORMat:TRACe:DATA?", + get_cmd=":FORMat:TRACe:DATA?", set_cmd=":FORMat:TRACe:DATA {}", val_mapping={ "ascii": "ASCii", "int32": "INTeger,32", "real32": "REAL,32", "real64": "REAL,64" - } + }, + docstring="Sets up format of data received" ) self.add_parameter( @@ -150,32 +219,25 @@ def __init__(self, name: str, address: str, **kwargs: Any) -> None: stop=self.stop, npts=self.npts, vals=Arrays(shape=(self.npts.get_latest,)), - parameter_class=FrequencyAxis + parameter_class=FrequencyAxis, + docstring="Sets frequency axis for the sweep." ) self.add_parameter( name="trace", + label="Trace", unit="dB", + number=1, vals=Arrays(shape=(self.npts.get_latest,)), setpoints=(self.freq_axis,), - parameter_class=Trace + parameter_class=Trace, + docstring="Gets trace data." ) - self.add_parameter( - name="cont_meas", - initial_value=False, - get_cmd=":INITiate:CONTinuous?", - set_cmd=self._enable_cont_meas, - val_mapping=create_on_off_val_mapping(on_val="ON", off_val="OFF") - ) - - self.connect_message() - - def _available_modes(self) -> Tuple[str, ...]: - available_modes = self.ask(":INSTrument:CATalog?") - return tuple(available_modes.split(',')) - def _set_start(self, val: float) -> None: + """ + Sets start frequency + """ stop = self.stop() if val >= stop: raise ValueError(f"Start frequency must be smaller than stop " @@ -191,6 +253,9 @@ def _set_start(self, val: float) -> None: ) def _set_stop(self, val: float) -> None: + """ + Sets stop frequency + """ start = self.start() if val <= start: raise ValueError(f"Stop frequency must be larger than start " @@ -206,67 +271,85 @@ def _set_stop(self, val: float) -> None: ) def _set_center(self, val: float) -> None: + """ + Sets center frequency and updates start and stop frequencies if they + change. + """ self.write(f":SENSe:FREQuency:CENTer {val}") self.update_trace() def _set_span(self, val: float) -> None: + """ + Sets frequency span and updates start and stop frequencies if they + change. + """ self.write(f":SENSe:FREQuency:SPAN {val}") self.update_trace() def _set_npts(self, val: int) -> None: + """ + Sets number of points for sweep + """ self.write(f":SENSe:SWEep:POINts {val}") def _enable_auto_sweep_time(self, val: str) -> None: + """ + Enables auto sweep time + """ self.write(f":SENSe:SWEep:TIME:AUTO {val}") def _enable_auto_sweep_type(self, val: str) -> None: + """ + Enables auto sweep type + """ self.write(f":SENSe:SWEep:TYPE:AUTO {val}") def _set_sweep_type(self, val: str) -> None: + """ + Sets sweep type + """ self.write(f":SENSe:SWEep:TYPE {val}") - def _enable_cont_meas(self, val: str) -> None: - self.write(f":INITiate:CONTinuous {val}") - - def _get_data(self) -> ParamRawDataType: - - # trace number - n: int = 2 - with self.status.set_to(1): - self.cont_meas("OFF") - try: - timeout = self.sweep_time() + self._additional_wait - with self.timeout.set_to(timeout): - data_str = self.ask(f":READ:" - f"{self.measurement}{n}?") + def _get_data(self, trace_num: int) -> ParamRawDataType: + """ + Gets data from the measurement. + """ + self.cont_meas("OFF") + try: + timeout = self.sweep_time() + self._additional_wait + with self.timeout.set_to(timeout): + data_str = self.ask(f":READ:{self.measurement}{trace_num}?") data = np.array(data_str.rstrip()).astype("float64") - finally: - self.cont_meas("ON") + finally: + self.cont_meas("ON") + return data def update_trace(self) -> None: + """ + Updates start and stop frequencies whenever span of/or center frequency + is updated. + """ self.start() self.stop() - def setup_sweep(self, start: float, stop: float, npts: int) -> None: - self.mode("SA") - self.measurement("Swept SA") + def setup_swept_sa_sweep(self, + start: float, + stop: float, + npts: int) -> None: + """ + Sets up the Swept SA measurement sweep for Spectrum Analyzer Mode. + """ + if "SANalyzer" in self._available_meas(): + self.measurement("SANalyzer") + else: + raise RuntimeError("Swept SA measurement is not available on your " + "Keysight N9030B instrument with Spectrum " + "Analyzer mode.") self.start(start) self.stop(stop) self.npts(npts) - def reset(self) -> None: - """ - Reset the instrument by sending the RST command - """ - self.write("*RST") - - def abort(self) -> None: - """ - Aborts the measurement - """ - self.write(":ABORt") - def autotune(self) -> None: """ Autotunes frequency From aa542496ffc80e99f12d5c8c70016f1bc27da66e Mon Sep 17 00:00:00 2001 From: Akshita07 Date: Thu, 14 Jan 2021 11:02:41 +0100 Subject: [PATCH 12/81] Add doc strings --- qcodes/instrument_drivers/Keysight/N9030B.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/qcodes/instrument_drivers/Keysight/N9030B.py b/qcodes/instrument_drivers/Keysight/N9030B.py index 19f6f0923cd3..90659a5e3a2c 100644 --- a/qcodes/instrument_drivers/Keysight/N9030B.py +++ b/qcodes/instrument_drivers/Keysight/N9030B.py @@ -49,7 +49,9 @@ def __init__(self, name: str, address: str, **kwargs: Any) -> None: name="mode", get_cmd=":INSTrument:SELect?", set_cmd=":INSTrument:SELect {}", - vals=Enum(*self._available_modes()) + vals=Enum(*self._available_modes()), + docstring="Allows setting of different modes present and licensed " + "for the instrument." ) self.add_parameter( @@ -57,12 +59,16 @@ def __init__(self, name: str, address: str, **kwargs: Any) -> None: initial_value=False, get_cmd=":INITiate:CONTinuous?", set_cmd=self._enable_cont_meas, - val_mapping=create_on_off_val_mapping(on_val="ON", off_val="OFF") + val_mapping=create_on_off_val_mapping(on_val="ON", off_val="OFF"), + docstring="Enables or disables continuous measurement." ) self.connect_message() def _available_modes(self) -> Tuple[str, ...]: + """ + Returns present and licensed modes for the instrument. + """ available_modes = self.ask(":INSTrument:CATalog?") return tuple(available_modes.split(',')) @@ -74,6 +80,9 @@ def _available_meas(self) -> Tuple[str, ...]: return tuple(available_meas.split(',')) def _enable_cont_meas(self, val: str) -> None: + """ + Sets continuous measurement to ON or OFF. + """ self.write(f":INITiate:CONTinuous {val}") def reset(self) -> None: From 9936a99439d3f73b87d9167840cdd67ea969f332 Mon Sep 17 00:00:00 2001 From: Akshita07 Date: Thu, 14 Jan 2021 11:24:51 +0100 Subject: [PATCH 13/81] Add phase noise mode class --- qcodes/instrument_drivers/Keysight/N9030B.py | 43 +++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/qcodes/instrument_drivers/Keysight/N9030B.py b/qcodes/instrument_drivers/Keysight/N9030B.py index 90659a5e3a2c..8f3e58df400a 100644 --- a/qcodes/instrument_drivers/Keysight/N9030B.py +++ b/qcodes/instrument_drivers/Keysight/N9030B.py @@ -100,7 +100,7 @@ def abort(self) -> None: class SpectrumAnalyzer(N9030B): """ - Spectrum Analyzer Mode for Kyesight N9030B instrument. + Spectrum Analyzer Mode for Keysight N9030B instrument. """ def __init__(self, name: str, *arg, **kwargs): super().__init__(name, *arg, **kwargs) @@ -364,3 +364,44 @@ def autotune(self) -> None: Autotunes frequency """ self.write(":SENS:FREQuency:TUNE:IMMediate") + + +class PhaseNoise(N9030B): + """ + Phase Noise Mode for Keysight N9030B instrument. + """ + + def __init__(self, name: str, *arg, **kwargs): + super().__init__(name, *arg, **kwargs) + + if "PNOISE" in self._available_modes(): + self.mode("PNOISE") + else: + raise RuntimeError("Phase Noise Mode is not available on " + "your Keysight N9030B instrument.") + + self.add_parameter( + name="npts", + get_cmd=":SENSe:LPLot:SWEep:POINts?", + set_cmd=":SENSe:LPLot:SWEep:POINts {}", + get_parser=int, + vals=Ints(601, 20001), + docstring="Number of points for the sweep" + ) + + def setup_log_plot_sweep(self) -> None: + """ + Sets up the Log Plot measurement sweep for Phase Noise Mode. + """ + if "LPLot" in self._available_meas(): + self.measurement("LPLot") + else: + raise RuntimeError("Log Plot measurement is not available on your " + "Keysight N9030B instrument with Phase Noise " + "mode.") + + def autotune(self) -> None: + """ + Autotunes frequency + """ + self.write(":SENSe:FREQuency:CARRier:SEARch") From c554e53348c6bc72fe1dae47153154b612138d77 Mon Sep 17 00:00:00 2001 From: Akshita07 Date: Thu, 14 Jan 2021 11:29:29 +0100 Subject: [PATCH 14/81] Move measurement param to base class --- qcodes/instrument_drivers/Keysight/N9030B.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/qcodes/instrument_drivers/Keysight/N9030B.py b/qcodes/instrument_drivers/Keysight/N9030B.py index 8f3e58df400a..242b8f55147b 100644 --- a/qcodes/instrument_drivers/Keysight/N9030B.py +++ b/qcodes/instrument_drivers/Keysight/N9030B.py @@ -54,6 +54,15 @@ def __init__(self, name: str, address: str, **kwargs: Any) -> None: "for the instrument." ) + self.add_parameter( + name="measurement", + get_cmd=":CONFigure?", + set_cmd=":CONFigure:{}", + vals=Enum(*self._available_meas()), + docstring="Sets measurement type from among the available " + "measurement types." + ) + self.add_parameter( name="cont_meas", initial_value=False, @@ -114,15 +123,6 @@ def __init__(self, name: str, *arg, **kwargs): self._min_freq = 2 self._max_freq = 50e9 - self.add_parameter( - name="measurement", - get_cmd=":CONFigure?", - set_cmd=":CONFigure:{}", - vals=Enum(*self._available_meas()), - docstring="Sets measurement type from among the available " - "measurement types." - ) - self.add_parameter( name="start", get_cmd=":SENSe:FREQuency:STARt?", From 379ac7422b228f36fc1c0bca261ffe856057f465 Mon Sep 17 00:00:00 2001 From: Akshita07 Date: Thu, 14 Jan 2021 11:38:01 +0100 Subject: [PATCH 15/81] Add missing type annotations --- qcodes/instrument_drivers/Keysight/N9030B.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qcodes/instrument_drivers/Keysight/N9030B.py b/qcodes/instrument_drivers/Keysight/N9030B.py index 242b8f55147b..8af6fee9f864 100644 --- a/qcodes/instrument_drivers/Keysight/N9030B.py +++ b/qcodes/instrument_drivers/Keysight/N9030B.py @@ -111,7 +111,7 @@ class SpectrumAnalyzer(N9030B): """ Spectrum Analyzer Mode for Keysight N9030B instrument. """ - def __init__(self, name: str, *arg, **kwargs): + def __init__(self, name: str, *arg: Any, **kwargs: Any): super().__init__(name, *arg, **kwargs) if "SA" in self._available_modes(): @@ -371,7 +371,7 @@ class PhaseNoise(N9030B): Phase Noise Mode for Keysight N9030B instrument. """ - def __init__(self, name: str, *arg, **kwargs): + def __init__(self, name: str, *arg: Any, **kwargs: Any): super().__init__(name, *arg, **kwargs) if "PNOISE" in self._available_modes(): From 6390a38765ccea9efe081a4d19e07c2609e6d8ff Mon Sep 17 00:00:00 2001 From: Akshita07 Date: Thu, 14 Jan 2021 13:13:48 +0100 Subject: [PATCH 16/81] Add params to Phase noise mode --- qcodes/instrument_drivers/Keysight/N9030B.py | 154 +++++++++++++++++-- 1 file changed, 138 insertions(+), 16 deletions(-) diff --git a/qcodes/instrument_drivers/Keysight/N9030B.py b/qcodes/instrument_drivers/Keysight/N9030B.py index 8af6fee9f864..c83744e5d965 100644 --- a/qcodes/instrument_drivers/Keysight/N9030B.py +++ b/qcodes/instrument_drivers/Keysight/N9030B.py @@ -1,5 +1,5 @@ import numpy as np -from typing import Any, Tuple +from typing import Any, Tuple, Dict, Union from qcodes import VisaInstrument, Parameter, ParameterWithSetpoints from qcodes.instrument.parameter import ParamRawDataType @@ -24,7 +24,7 @@ class Trace(ParameterWithSetpoints): def __init__(self, number: int, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) - self.instrument: "SpectrumAnalyzer" + self.instrument: Union["SpectrumAnalyzer", "PhaseNoise"] self.root_instrument: "N9030B" self.n = number @@ -72,6 +72,18 @@ def __init__(self, name: str, address: str, **kwargs: Any) -> None: docstring="Enables or disables continuous measurement." ) + self.add_parameter( + name="format", + get_cmd=":FORMat:TRACe:DATA?", + set_cmd=":FORMat:TRACe:DATA {}", + val_mapping={ + "ascii": "ASCii", + "int32": "INTeger,32", + "real32": "REAL,32", + "real64": "REAL,64" + }, + docstring="Sets up format of data received" + ) self.connect_message() def _available_modes(self) -> Tuple[str, ...]: @@ -94,6 +106,13 @@ def _enable_cont_meas(self, val: str) -> None: """ self.write(f":INITiate:CONTinuous {val}") + def _options(self) -> Tuple[str, ...]: + """ + Returns installed options numbers. + """ + options_raw = self.ask('*OPT?') + return tuple(options_raw.split(',')) + def reset(self) -> None: """ Reset the instrument by sending the RST command @@ -207,19 +226,6 @@ def __init__(self, name: str, *arg: Any, **kwargs: Any): "'sweep'." ) - self.add_parameter( - name="format", - get_cmd=":FORMat:TRACe:DATA?", - set_cmd=":FORMat:TRACe:DATA {}", - val_mapping={ - "ascii": "ASCii", - "int32": "INTeger,32", - "real32": "REAL,32", - "real64": "REAL,64" - }, - docstring="Sets up format of data received" - ) - self.add_parameter( name="freq_axis", label="Frequency", @@ -380,6 +386,19 @@ def __init__(self, name: str, *arg: Any, **kwargs: Any): raise RuntimeError("Phase Noise Mode is not available on " "your Keysight N9030B instrument.") + self._min_freq = 1 + + self._valid_max_freq: Dict[str: float] = {"503": 3699999995, + "508": 8499999995, + "513": 13799999995, + "526": 26999999995} + opt: str + for x in self._valid_max_freq.keys(): + if x in self._options(): + opt = x + + self._max_freq = self._valid_max_freq[opt] + self.add_parameter( name="npts", get_cmd=":SENSe:LPLot:SWEep:POINts?", @@ -389,7 +408,106 @@ def __init__(self, name: str, *arg: Any, **kwargs: Any): docstring="Number of points for the sweep" ) - def setup_log_plot_sweep(self) -> None: + self.add_parameter( + name="start_offset", + get_cmd=":SENSe:LPLot:FREQuency:OFFSet:STARt?", + set_cmd=self._set_start_offset, + get_parser=float, + vals=Numbers(self._min_freq, self._max_freq - 10), + docstring="start frequency offset for the plot" + ) + + self.add_parameter( + name="stop_offset", + get_cmd=":SENSe:LPLot:FREQuency:OFFSet:STOP?", + set_cmd=self._set_stop_offset, + get_parser=float, + vals=Numbers(self._min_freq + 100, self._max_freq), + docstring="stop frequency offset for the plot" + ) + + self.add_parameter( + name="freq_axis", + label="Frequency", + unit="Hz", + start=self.start_offset, + stop=self.stop_offset, + npts=self.npts, + vals=Arrays(shape=(self.npts.get_latest,)), + parameter_class=FrequencyAxis, + docstring="Sets frequency axis for the sweep." + ) + + self.add_parameter( + name="trace", + label="Trace", + unit="dB", + number=1, + vals=Arrays(shape=(self.npts.get_latest,)), + setpoints=(self.freq_axis,), + parameter_class=Trace, + docstring="Gets trace data." + ) + + def _set_start_offset(self, val: float) -> None: + """ + Sets start offset for frequency in the plot + """ + stop_offset = self.stop_offset() + self.write(f":SENSe:LPLot:FREQuency:OFFSet:STARt {val}") + start_offset = self.start_offset() + + if abs(val - start_offset) >= 1: + self.log.warning( + f"Could not set start offset to {val} setting it to " + f"{start_offset}" + ) + if val >= stop_offset or abs(val - stop_offset) < 10: + self.log.warning(f"Provided start frequency offset {val} Hz was " + f"greater than preset stop frequency offset " + f"{stop_offset} Hz. Provided start frequency " + f"offset {val} Hz is set and new stop freq offset" + f" is: {self.stop_offset()} Hz.") + + def _set_stop_offset(self, val: float) -> None: + """ + Sets stop offset for frequency in the plot + """ + start_offset = self.start_offset() + self.write(f":SENSe:LPLot:FREQuency:OFFSet:STOP {val}") + stop_offset = self.stop_offset() + + if abs(val - stop_offset) >= 1: + self.log.warning( + f"Could not set stop offset to {val} setting it to " + f"{stop_offset}" + ) + + if val <= start_offset or abs(val-start_offset) < 10: + raise ValueError(f"Provided stop frequency offset {val} Hz was " + f"less than preset start frequency offset " + f"{start_offset} Hz. Provided stop frequency " + f"offset {val} Hz is set and new start freq offset" + f" is: {self.start_offset()} Hz.") + + def _get_data(self, trace_num: int) -> ParamRawDataType: + """ + Gets data from the measurement. + """ + self.cont_meas("OFF") + try: + data_str = self.ask(f":READ:{self.measurement}{trace_num}?") + data = np.array(data_str.rstrip()).astype("float64") + finally: + self.cont_meas("ON") + + return data + + def setup_log_plot_sweep(self, + start_offset: float, + stop_offset: float, + npts: int + ) -> None: """ Sets up the Log Plot measurement sweep for Phase Noise Mode. """ @@ -400,6 +518,10 @@ def setup_log_plot_sweep(self) -> None: "Keysight N9030B instrument with Phase Noise " "mode.") + self.start_offset(start_offset) + self.stop_offset(stop_offset) + self.npts(npts) + def autotune(self) -> None: """ Autotunes frequency From 3287dcda724cad2853e432567d51c1d8d209b14e Mon Sep 17 00:00:00 2001 From: Akshita07 Date: Thu, 14 Jan 2021 14:06:04 +0100 Subject: [PATCH 17/81] fix type hint --- qcodes/instrument_drivers/Keysight/N9030B.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qcodes/instrument_drivers/Keysight/N9030B.py b/qcodes/instrument_drivers/Keysight/N9030B.py index c83744e5d965..c77cf8cc5950 100644 --- a/qcodes/instrument_drivers/Keysight/N9030B.py +++ b/qcodes/instrument_drivers/Keysight/N9030B.py @@ -388,7 +388,7 @@ def __init__(self, name: str, *arg: Any, **kwargs: Any): self._min_freq = 1 - self._valid_max_freq: Dict[str: float] = {"503": 3699999995, + self._valid_max_freq: Dict[str, float] = {"503": 3699999995, "508": 8499999995, "513": 13799999995, "526": 26999999995} From 4ebb80069bd42de32d7b0eab0725d0107a159767 Mon Sep 17 00:00:00 2001 From: Akshita07 Date: Thu, 14 Jan 2021 14:50:33 +0100 Subject: [PATCH 18/81] Add yaml and test file --- qcodes/instrument/sims/Keysight_N9030B.yaml | 48 ++++++++++++++++++++ qcodes/tests/drivers/test_keysight_n9030b.py | 22 +++++++++ 2 files changed, 70 insertions(+) create mode 100644 qcodes/instrument/sims/Keysight_N9030B.yaml create mode 100644 qcodes/tests/drivers/test_keysight_n9030b.py diff --git a/qcodes/instrument/sims/Keysight_N9030B.yaml b/qcodes/instrument/sims/Keysight_N9030B.yaml new file mode 100644 index 000000000000..5b672aee52b8 --- /dev/null +++ b/qcodes/instrument/sims/Keysight_N9030B.yaml @@ -0,0 +1,48 @@ +spec: "1.0" +devices: + KeysightN9030B: + eom: + GPIB INSTR: + q: "\n" + r: "\n" + error: ERROR + dialogues: + - q: "*RST" + - q: ":ABORt" + - q: ":INSTrument:CATalog?" + r: "SA,PNOISE" + - q: ":CONFigure:CATalog?" + r: "SANalyzer,LPLot" + - q: "*OPT?" + r: "526" + - q: "*IDN?" + r: "Keysight Technologies,N9030B,1000,0.1" + + properties: + + mode: + default: "SA" + getter: + q: ":INSTrument:SELect?" + r: "{}" + setter: + q: ":INSTrument:SELect {}" + + measurement: + default: "SAN" + getter: + q: ":CONFigure?" + r: "{}" + setter: + q: ":CONFigure:{}" + + cont_meas: + getter: + q: ":INITiate:CONTinuous?" + r: "{}" + setter: + q: ":INITiate:CONTinuous {}" + +resources: + GPIB::1::INSTR: + device: KeysightN9030B \ No newline at end of file diff --git a/qcodes/tests/drivers/test_keysight_n9030b.py b/qcodes/tests/drivers/test_keysight_n9030b.py new file mode 100644 index 000000000000..d2a4c4147e1c --- /dev/null +++ b/qcodes/tests/drivers/test_keysight_n9030b.py @@ -0,0 +1,22 @@ +import pytest + +import qcodes.instrument_drivers.Keysight.N9030B as N9030B +import qcodes.instrument.sims as sims + +VISALIB = sims.__file__.replace('__init__.py', 'Keysight_N9030B.yaml@sim') + + +@pytest.fixture(name="driver") +def _make_driver(): + driver = N9030B.N9030B('n9030B_sim', + address="GPIB::1::INSTR", + visalib=VISALIB) + yield driver + driver.close() + + +def test_idn(driver): + assert {'firmware': '0.1', + 'model': 'N9030B', + 'serial': '1000', + 'vendor': 'Keysight Technologies'} == driver.IDN() From 0670293a93f2ba5dbfe63057f452ab31225f62a7 Mon Sep 17 00:00:00 2001 From: Akshita07 Date: Thu, 14 Jan 2021 14:53:16 +0100 Subject: [PATCH 19/81] Add empty line in yaml file --- qcodes/instrument/sims/Keysight_N9030B.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qcodes/instrument/sims/Keysight_N9030B.yaml b/qcodes/instrument/sims/Keysight_N9030B.yaml index 5b672aee52b8..0dc133707638 100644 --- a/qcodes/instrument/sims/Keysight_N9030B.yaml +++ b/qcodes/instrument/sims/Keysight_N9030B.yaml @@ -45,4 +45,4 @@ devices: resources: GPIB::1::INSTR: - device: KeysightN9030B \ No newline at end of file + device: KeysightN9030B From 9d4092f2f3f5ffa103a817faa1e403681185c79c Mon Sep 17 00:00:00 2001 From: Akshita07 Date: Thu, 14 Jan 2021 16:49:34 +0100 Subject: [PATCH 20/81] Add driver example notebook --- .../Qcodes example with Keysight N9030B.ipynb | 259 ++++++++++++++++++ 1 file changed, 259 insertions(+) create mode 100644 docs/examples/driver_examples/Qcodes example with Keysight N9030B.ipynb diff --git a/docs/examples/driver_examples/Qcodes example with Keysight N9030B.ipynb b/docs/examples/driver_examples/Qcodes example with Keysight N9030B.ipynb new file mode 100644 index 000000000000..b343c94c353d --- /dev/null +++ b/docs/examples/driver_examples/Qcodes example with Keysight N9030B.ipynb @@ -0,0 +1,259 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Qcodes example with Keysight N9030B" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This notebook shows how to use Keysight N9030B instrument driver in Spectrum Analyzer and Phase Noise Modes for Swept SA and Log Plot measurements respectively.\n", + "\n", + "Let's begin!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "ExecuteTime": { + "end_time": "2021-01-14T15:11:25.180448Z", + "start_time": "2021-01-14T15:11:21.379687Z" + } + }, + "outputs": [], + "source": [ + "import numpy as np\n", + "from qcodes.dataset import initialise_or_create_database_at, load_or_create_experiment, Measurement\n", + "from qcodes.dataset.plotting import plot_dataset, plot_by_id\n", + "\n", + "from qcodes.instrument_drivers.Keysight.N9030B import N9030B, SpectrumAnalyzer, PhaseNoise" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "instr = N9030B(\"n9030b\",\"GPIB_Address\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "instr.IDN()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Spectrum Analyzer mode with Swept SA measurement" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "sa = SpectrumAnalyzer(\"sa\", address=\"GPIB_Address\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "sa.setup_swept_sa_sweep(start=200, stop= 10e3, npts=20001)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### With QCoDeS Measurement" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Initialize database and begin experiment..." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "tutorial_db_path = os.path.join(os.getcwd(), 'tutorial.db')\n", + "initialise_or_create_database_at(tutorial_db_path)\n", + "load_or_create_experiment(experiment_name='tutorial_exp', sample_name=\"no sample\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "meas1 = Measurement()\n", + "meas1.register_parameter(sa.trace)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "with meas1.run() as datasaver:\n", + " datasaver.add_result((sa.trace, sa.trace.get()))\n", + "\n", + "dataset = datasaver.dataset" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Plot data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "_ = plot_dataset(dataset)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Phase Noise mode with Log Plot measurement" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "pn = PhaseNoise('pn', address=\"GPIB_Address\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "pn.setup_log_plot_sweep(start_offset=10, stop_offset=200, npts=1001)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### With QCoDeS Measurement" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "meas2 = Measurement()\n", + "meas2.register_parameter(pn.trace)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "with meas1.run() as datasaver:\n", + " datasaver.add_result((pn.trace, pn.trace.get()))\n", + "\n", + "run_id = datasaver.run_id" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Plot data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "_ = plot_by_id(run_id)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Live plotting with Plottr" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "For live plotting, you can use Plottr package with Qcodes. For more information about this, head to [How to use plottr with QCoDeS for live plotting](./plotting/How-to-use-Plottr-with-QCoDeS-for-live-plotting.ipynb)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "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.7.9" + }, + "nbsphinx": { + "execute": "never" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} From 156e5870bc55c4b7562a3fff4e3262e5df5d3987 Mon Sep 17 00:00:00 2001 From: Akshita07 Date: Thu, 14 Jan 2021 17:15:49 +0100 Subject: [PATCH 21/81] Fix link in notebook --- .../driver_examples/Qcodes example with Keysight N9030B.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/examples/driver_examples/Qcodes example with Keysight N9030B.ipynb b/docs/examples/driver_examples/Qcodes example with Keysight N9030B.ipynb index b343c94c353d..3e573f041281 100644 --- a/docs/examples/driver_examples/Qcodes example with Keysight N9030B.ipynb +++ b/docs/examples/driver_examples/Qcodes example with Keysight N9030B.ipynb @@ -221,7 +221,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "For live plotting, you can use Plottr package with Qcodes. For more information about this, head to [How to use plottr with QCoDeS for live plotting](./plotting/How-to-use-Plottr-with-QCoDeS-for-live-plotting.ipynb)." + "For live plotting, you can use Plottr package with Qcodes. For more information about this, head to [How to use plottr with QCoDeS for live plotting](../plotting/How-to-use-Plottr-with-QCoDeS-for-live-plotting.ipynb)." ] }, { From 19b6699a58a5292c375899a2319ebff3ef225130 Mon Sep 17 00:00:00 2001 From: Akshita07 Date: Fri, 15 Jan 2021 13:40:43 +0100 Subject: [PATCH 22/81] Make SA and PNoise modes as submodules --- qcodes/instrument_drivers/Keysight/N9030B.py | 239 ++++++++++--------- 1 file changed, 126 insertions(+), 113 deletions(-) diff --git a/qcodes/instrument_drivers/Keysight/N9030B.py b/qcodes/instrument_drivers/Keysight/N9030B.py index c77cf8cc5950..f6d3cba74344 100644 --- a/qcodes/instrument_drivers/Keysight/N9030B.py +++ b/qcodes/instrument_drivers/Keysight/N9030B.py @@ -1,7 +1,8 @@ import numpy as np from typing import Any, Tuple, Dict, Union -from qcodes import VisaInstrument, Parameter, ParameterWithSetpoints +from qcodes import (VisaInstrument, InstrumentChannel, Parameter, + ParameterWithSetpoints, ChannelList) from qcodes.instrument.parameter import ParamRawDataType from qcodes.utils.validators import Enum, Numbers, Arrays, Ints from qcodes.utils.helpers import create_on_off_val_mapping @@ -33,111 +34,14 @@ def get_raw(self) -> ParamRawDataType: return self.instrument._get_data(trace_num=self.n) -class N9030B(VisaInstrument): - """ - Driver for Keysight N9030B PXA signal analyzer. - """ - - def __init__(self, name: str, address: str, **kwargs: Any) -> None: - super().__init__(name, address, terminator='\n', **kwargs) - - self._min_freq: float - self._max_freq: float - self._additional_wait: float = 1 - - self.add_parameter( - name="mode", - get_cmd=":INSTrument:SELect?", - set_cmd=":INSTrument:SELect {}", - vals=Enum(*self._available_modes()), - docstring="Allows setting of different modes present and licensed " - "for the instrument." - ) - - self.add_parameter( - name="measurement", - get_cmd=":CONFigure?", - set_cmd=":CONFigure:{}", - vals=Enum(*self._available_meas()), - docstring="Sets measurement type from among the available " - "measurement types." - ) - - self.add_parameter( - name="cont_meas", - initial_value=False, - get_cmd=":INITiate:CONTinuous?", - set_cmd=self._enable_cont_meas, - val_mapping=create_on_off_val_mapping(on_val="ON", off_val="OFF"), - docstring="Enables or disables continuous measurement." - ) - - self.add_parameter( - name="format", - get_cmd=":FORMat:TRACe:DATA?", - set_cmd=":FORMat:TRACe:DATA {}", - val_mapping={ - "ascii": "ASCii", - "int32": "INTeger,32", - "real32": "REAL,32", - "real64": "REAL,64" - }, - docstring="Sets up format of data received" - ) - self.connect_message() - - def _available_modes(self) -> Tuple[str, ...]: - """ - Returns present and licensed modes for the instrument. - """ - available_modes = self.ask(":INSTrument:CATalog?") - return tuple(available_modes.split(',')) - - def _available_meas(self) -> Tuple[str, ...]: - """ - Gives available measurement with a given mode for the instrument - """ - available_meas = self.ask(":CONFigure:CATalog?") - return tuple(available_meas.split(',')) - - def _enable_cont_meas(self, val: str) -> None: - """ - Sets continuous measurement to ON or OFF. - """ - self.write(f":INITiate:CONTinuous {val}") - - def _options(self) -> Tuple[str, ...]: - """ - Returns installed options numbers. - """ - options_raw = self.ask('*OPT?') - return tuple(options_raw.split(',')) - - def reset(self) -> None: - """ - Reset the instrument by sending the RST command - """ - self.write("*RST") - - def abort(self) -> None: - """ - Aborts the measurement - """ - self.write(":ABORt") - - -class SpectrumAnalyzer(N9030B): +class SpectrumAnalyzer(InstrumentChannel): """ Spectrum Analyzer Mode for Keysight N9030B instrument. """ - def __init__(self, name: str, *arg: Any, **kwargs: Any): - super().__init__(name, *arg, **kwargs) + def __init__(self, parent: "N9030B", name: str, *arg: Any, **kwargs: Any): + super().__init__(parent, name, *arg, **kwargs) - if "SA" in self._available_modes(): - self.mode("SA") - else: - raise RuntimeError("Spectrum Analyzer Mode is not available on " - "your Keysight N9030B instrument.") + self.mode("SA") self._min_freq = 2 self._max_freq = 50e9 @@ -372,22 +276,17 @@ def autotune(self) -> None: self.write(":SENS:FREQuency:TUNE:IMMediate") -class PhaseNoise(N9030B): +class PhaseNoise(InstrumentChannel): """ Phase Noise Mode for Keysight N9030B instrument. """ - def __init__(self, name: str, *arg: Any, **kwargs: Any): - super().__init__(name, *arg, **kwargs) + def __init__(self, parent: "N9030B", name: str, *arg: Any, **kwargs: Any): + super().__init__(parent, name, *arg, **kwargs) - if "PNOISE" in self._available_modes(): - self.mode("PNOISE") - else: - raise RuntimeError("Phase Noise Mode is not available on " - "your Keysight N9030B instrument.") + self.mode("PNOISE") self._min_freq = 1 - self._valid_max_freq: Dict[str, float] = {"503": 3699999995, "508": 8499999995, "513": 13799999995, @@ -396,7 +295,6 @@ def __init__(self, name: str, *arg: Any, **kwargs: Any): for x in self._valid_max_freq.keys(): if x in self._options(): opt = x - self._max_freq = self._valid_max_freq[opt] self.add_parameter( @@ -484,7 +382,7 @@ def _set_stop_offset(self, val: float) -> None: ) if val <= start_offset or abs(val-start_offset) < 10: - raise ValueError(f"Provided stop frequency offset {val} Hz was " + self.log.warning(f"Provided stop frequency offset {val} Hz was " f"less than preset start frequency offset " f"{start_offset} Hz. Provided stop frequency " f"offset {val} Hz is set and new start freq offset" @@ -527,3 +425,118 @@ def autotune(self) -> None: Autotunes frequency """ self.write(":SENSe:FREQuency:CARRier:SEARch") + + +class N9030B(VisaInstrument): + """ + Driver for Keysight N9030B PXA signal analyzer. + """ + + CHANNEL_CLASS_1 = SpectrumAnalyzer + CHANNEL_CLASS_2 = PhaseNoise + + def __init__(self, name: str, address: str, **kwargs: Any) -> None: + super().__init__(name, address, terminator='\n', **kwargs) + + self._min_freq: float + self._max_freq: float + self._additional_wait: float = 1 + + self.add_parameter( + name="mode", + get_cmd=":INSTrument:SELect?", + set_cmd=":INSTrument:SELect {}", + vals=Enum(*self._available_modes()), + docstring="Allows setting of different modes present and licensed " + "for the instrument." + ) + + self.add_parameter( + name="measurement", + get_cmd=":CONFigure?", + set_cmd=":CONFigure:{}", + vals=Enum(*self._available_meas()), + docstring="Sets measurement type from among the available " + "measurement types." + ) + + self.add_parameter( + name="cont_meas", + initial_value=False, + get_cmd=":INITiate:CONTinuous?", + set_cmd=self._enable_cont_meas, + val_mapping=create_on_off_val_mapping(on_val="ON", off_val="OFF"), + docstring="Enables or disables continuous measurement." + ) + + self.add_parameter( + name="format", + get_cmd=":FORMat:TRACe:DATA?", + set_cmd=":FORMat:TRACe:DATA {}", + val_mapping={ + "ascii": "ASCii", + "int32": "INTeger,32", + "real32": "REAL,32", + "real64": "REAL,64" + }, + docstring="Sets up format of data received" + ) + + if "SA" in self._available_modes(): + sa_mode = ChannelList( + self, "SA", self.CHANNEL_CLASS_1, snapshotable=True + ) + self.add_submodule("SA", sa_mode) + else: + self.log.info("Spectrum Analyzer mode is not available on this " + "instrument.") + + if "PNOISE" in self._available_modes(): + pnoise_mode = ChannelList( + self, "PNoise", self.CHANNEL_CLASS_2, snapshotable=True + ) + self.add_submodule("PNoise", pnoise_mode) + else: + self.log.info("Phase Noise mode is not available on this " + "instrument.") + self.reset() + self.connect_message() + + def _available_modes(self) -> Tuple[str, ...]: + """ + Returns present and licensed modes for the instrument. + """ + available_modes = self.ask(":INSTrument:CATalog?") + return tuple(available_modes.split(',')) + + def _available_meas(self) -> Tuple[str, ...]: + """ + Gives available measurement with a given mode for the instrument + """ + available_meas = self.ask(":CONFigure:CATalog?") + return tuple(available_meas.split(',')) + + def _enable_cont_meas(self, val: str) -> None: + """ + Sets continuous measurement to ON or OFF. + """ + self.write(f":INITiate:CONTinuous {val}") + + def _options(self) -> Tuple[str, ...]: + """ + Returns installed options numbers. + """ + options_raw = self.ask('*OPT?') + return tuple(options_raw.split(',')) + + def reset(self) -> None: + """ + Reset the instrument by sending the RST command + """ + self.write("*RST") + + def abort(self) -> None: + """ + Aborts the measurement + """ + self.write(":ABORt") From f8f6384c1db65ee17a38f48b271f8a6373fcc2b4 Mon Sep 17 00:00:00 2001 From: Akshita07 Date: Fri, 15 Jan 2021 15:40:29 +0100 Subject: [PATCH 23/81] Add submodules to yaml --- qcodes/instrument/sims/Keysight_N9030B.yaml | 146 ++++++++++++++++++++ 1 file changed, 146 insertions(+) diff --git a/qcodes/instrument/sims/Keysight_N9030B.yaml b/qcodes/instrument/sims/Keysight_N9030B.yaml index 0dc133707638..6aa21507b366 100644 --- a/qcodes/instrument/sims/Keysight_N9030B.yaml +++ b/qcodes/instrument/sims/Keysight_N9030B.yaml @@ -43,6 +43,152 @@ devices: setter: q: ":INITiate:CONTinuous {}" + submodules: + SA: + dialogues: + - q: ":SENS:FREQuency:TUNE:IMMediate" + + properties: + + start: + getter: + q: ":SENSe:FREQuency:STARt?" + r: "{}" + setter: + q: ":SENSe:FREQuency:STARt {}" + specs: + min: 2 + max: 49999999990 + type: float + + stop: + getter: + q: ":SENSe:FREQuency:STOP?" + r: "{}" + setter: + q: ":SENSe:FREQuency:STOP {}" + specs: + min: 12 + max: 50e9 + type: float + + center: + getter: + q: ":SENSe:FREQuency:CENTer?" + r: "{}" + setter: + q: ":SENSe:FREQuency:CENTer {}" + specs: + min: 7 + max: 49999999995 + type: float + + span: + getter: + q: ":SENSe:FREQuency:SPAN?" + r: "{}" + setter: + q: ":SENSe:FREQuency:SPAN {}" + specs: + min: 10 + max: 49999999998 + type: float + + npts: + getter: + q: ":SENSe:SWEep:POINts?" + r: "{}" + setter: + q: ":SENSe:SWEep:POINts {}" + specs: + min: 1 + max: 20001 + type: int + + sweep_time: + getter: + q: ":SENSe:SWEep:TIME?" + r: "{}" + specs: + type: float + + auto_sweep_time_enabled: + default: False + getter: + q: ":SENSe:SWEep:TIME:AUTO?" + r: "{}" + setter: + q: ":SENSe:SWEep:TIME:AUTO {}" + specs: + type: bool + valid: [True, False] + + auto_sweep_type_enabled: + default: False + getter: + q: ":SENSe:SWEep:TYPE:AUTO?" + r: "{}" + setter: + q: ":SENSe:SWEep:TYPE:AUTO {}" + specs: + type: bool + valid: [True, False] + + sweep_type: + getter: + q: ":SENSe:SWEep:TYPE?" + r: "{}" + setter: + q: ":SENSe:SWEep:TYPE {}" + specs: + type: str + valid: ["FFT", "SWE"] + + trace: + getter: + q: ":READ:SANalyzer1?" + r: "{}" + specs: + type: float + + PNoise: + dialogues: + - q: ":SENSe:FREQuency:CARRier:SEARch" + + properties: + + npts: + getter: + q: ":SENSe:LPLot:SWEep:POINts?" + r: "{}" + setter: + q: ":SENSe:LPLot:SWEep:POINts {}" + specs: + min: 601 + max: 20001 + type: int + + start_offset: + getter: + q: ":SENSe:LPLot:FREQuency:OFFSet:STARt?" + r: "{}" + setter: + q: ":SENSe:LPLot:FREQuency:OFFSet:STARt {}" + + stop_offset: + getter: + q: ":SENSe:LPLot:FREQuency:OFFSet:STOP?" + r: "{}" + setter: + q: ":SENSe:LPLot:FREQuency:OFFSet:STOP {}" + + trace: + getter: + q: ":READ:LPLot1?" + r: "{}" + specs: + type: float + resources: GPIB::1::INSTR: device: KeysightN9030B From d58e16a64a9892f7120e74af6d3d2d0809b37ea9 Mon Sep 17 00:00:00 2001 From: Akshita07 Date: Fri, 15 Jan 2021 16:02:18 +0100 Subject: [PATCH 24/81] Update notebook --- .../driver_examples/Qcodes example with Keysight N9030B.ipynb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/examples/driver_examples/Qcodes example with Keysight N9030B.ipynb b/docs/examples/driver_examples/Qcodes example with Keysight N9030B.ipynb index 3e573f041281..d20658da8fe3 100644 --- a/docs/examples/driver_examples/Qcodes example with Keysight N9030B.ipynb +++ b/docs/examples/driver_examples/Qcodes example with Keysight N9030B.ipynb @@ -65,7 +65,7 @@ "metadata": {}, "outputs": [], "source": [ - "sa = SpectrumAnalyzer(\"sa\", address=\"GPIB_Address\")" + "sa = instr.SA()" ] }, { @@ -153,7 +153,7 @@ "metadata": {}, "outputs": [], "source": [ - "pn = PhaseNoise('pn', address=\"GPIB_Address\")" + "pn = instr.PNoise()" ] }, { From f1ab7f1dcf454820366765351dc1e77aed70dfef Mon Sep 17 00:00:00 2001 From: Akshita07 Date: Fri, 15 Jan 2021 17:14:05 +0100 Subject: [PATCH 25/81] Update yaml --- qcodes/instrument/sims/Keysight_N9030B.yaml | 40 +++++++++++---------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/qcodes/instrument/sims/Keysight_N9030B.yaml b/qcodes/instrument/sims/Keysight_N9030B.yaml index 6aa21507b366..9602ee984ea8 100644 --- a/qcodes/instrument/sims/Keysight_N9030B.yaml +++ b/qcodes/instrument/sims/Keysight_N9030B.yaml @@ -43,14 +43,16 @@ devices: setter: q: ":INITiate:CONTinuous {}" - submodules: + channels: SA: dialogues: - q: ":SENS:FREQuency:TUNE:IMMediate" + - q: ":READ:SANalyzer1?" properties: start: + default: 2 getter: q: ":SENSe:FREQuency:STARt?" r: "{}" @@ -62,6 +64,7 @@ devices: type: float stop: + default: 12 getter: q: ":SENSe:FREQuency:STOP?" r: "{}" @@ -73,6 +76,7 @@ devices: type: float center: + default: 1.805e9 getter: q: ":SENSe:FREQuency:CENTer?" r: "{}" @@ -84,6 +88,7 @@ devices: type: float span: + default: 3.59e9 getter: q: ":SENSe:FREQuency:SPAN?" r: "{}" @@ -95,6 +100,7 @@ devices: type: float npts: + default: 1001 getter: q: ":SENSe:SWEep:POINts?" r: "{}" @@ -106,6 +112,7 @@ devices: type: int sweep_time: + default: 20 getter: q: ":SENSe:SWEep:TIME?" r: "{}" @@ -113,28 +120,29 @@ devices: type: float auto_sweep_time_enabled: - default: False + default: "ON" getter: q: ":SENSe:SWEep:TIME:AUTO?" r: "{}" setter: q: ":SENSe:SWEep:TIME:AUTO {}" specs: - type: bool - valid: [True, False] + type: str + valid: ["ON", "OFF"] auto_sweep_type_enabled: - default: False + default: "OFF" getter: q: ":SENSe:SWEep:TYPE:AUTO?" r: "{}" setter: q: ":SENSe:SWEep:TYPE:AUTO {}" specs: - type: bool - valid: [True, False] + type: str + valid: ["ON", "OFF"] sweep_type: + default: "SWE" getter: q: ":SENSe:SWEep:TYPE?" r: "{}" @@ -144,20 +152,15 @@ devices: type: str valid: ["FFT", "SWE"] - trace: - getter: - q: ":READ:SANalyzer1?" - r: "{}" - specs: - type: float - PNoise: dialogues: - q: ":SENSe:FREQuency:CARRier:SEARch" + - q: ":READ:LPLot1?" properties: npts: + default: 601 getter: q: ":SENSe:LPLot:SWEep:POINts?" r: "{}" @@ -169,23 +172,22 @@ devices: type: int start_offset: + default: 100 getter: q: ":SENSe:LPLot:FREQuency:OFFSet:STARt?" r: "{}" setter: q: ":SENSe:LPLot:FREQuency:OFFSet:STARt {}" + specs: + type: float stop_offset: + default: 1e6 getter: q: ":SENSe:LPLot:FREQuency:OFFSet:STOP?" r: "{}" setter: q: ":SENSe:LPLot:FREQuency:OFFSet:STOP {}" - - trace: - getter: - q: ":READ:LPLot1?" - r: "{}" specs: type: float From 629c43353121c3af41548fd322e303d10a48e14a Mon Sep 17 00:00:00 2001 From: Akshita07 Date: Fri, 15 Jan 2021 17:14:35 +0100 Subject: [PATCH 26/81] Add mode fixtures to test file --- qcodes/tests/drivers/test_keysight_n9030b.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/qcodes/tests/drivers/test_keysight_n9030b.py b/qcodes/tests/drivers/test_keysight_n9030b.py index d2a4c4147e1c..3b180722ce78 100644 --- a/qcodes/tests/drivers/test_keysight_n9030b.py +++ b/qcodes/tests/drivers/test_keysight_n9030b.py @@ -15,6 +15,17 @@ def _make_driver(): driver.close() +@pytest.fixture(name="sa") +def _activate_sa_mode(driver): + sa = driver.SA + yield sa + +@pytest.fixture(name="pnoise") +def _activate_pnoise_mode(driver): + pn = driver.PNoise + yield pn + + def test_idn(driver): assert {'firmware': '0.1', 'model': 'N9030B', From 71f84eeeea98ffb08ae3e39d9787b1bcc79f70c2 Mon Sep 17 00:00:00 2001 From: Akshita <36688304+Akshita07@users.noreply.github.com> Date: Sun, 17 Jan 2021 10:49:39 +0100 Subject: [PATCH 27/81] Add mode to class name Co-authored-by: Trevor Morgan <63689909+trevormorgan@users.noreply.github.com> --- qcodes/instrument_drivers/Keysight/N9030B.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qcodes/instrument_drivers/Keysight/N9030B.py b/qcodes/instrument_drivers/Keysight/N9030B.py index f6d3cba74344..187e916d1a5d 100644 --- a/qcodes/instrument_drivers/Keysight/N9030B.py +++ b/qcodes/instrument_drivers/Keysight/N9030B.py @@ -34,7 +34,7 @@ def get_raw(self) -> ParamRawDataType: return self.instrument._get_data(trace_num=self.n) -class SpectrumAnalyzer(InstrumentChannel): +class SpectrumAnalyzerMode(InstrumentChannel): """ Spectrum Analyzer Mode for Keysight N9030B instrument. """ From 9a0d177934284e11e9bc9cc5ecf8917c12e502fc Mon Sep 17 00:00:00 2001 From: Akshita <36688304+Akshita07@users.noreply.github.com> Date: Sun, 17 Jan 2021 10:50:07 +0100 Subject: [PATCH 28/81] Add mode to class name Co-authored-by: Trevor Morgan <63689909+trevormorgan@users.noreply.github.com> --- qcodes/instrument_drivers/Keysight/N9030B.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qcodes/instrument_drivers/Keysight/N9030B.py b/qcodes/instrument_drivers/Keysight/N9030B.py index 187e916d1a5d..2d4ba19b2ed0 100644 --- a/qcodes/instrument_drivers/Keysight/N9030B.py +++ b/qcodes/instrument_drivers/Keysight/N9030B.py @@ -276,7 +276,7 @@ def autotune(self) -> None: self.write(":SENS:FREQuency:TUNE:IMMediate") -class PhaseNoise(InstrumentChannel): +class PhaseNoiseMode(InstrumentChannel): """ Phase Noise Mode for Keysight N9030B instrument. """ From f16e3facf8da9055a232a67c53aff57689415525 Mon Sep 17 00:00:00 2001 From: Akshita <36688304+Akshita07@users.noreply.github.com> Date: Sun, 17 Jan 2021 10:50:45 +0100 Subject: [PATCH 29/81] Rename variable Co-authored-by: Trevor Morgan <63689909+trevormorgan@users.noreply.github.com> --- qcodes/instrument_drivers/Keysight/N9030B.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/qcodes/instrument_drivers/Keysight/N9030B.py b/qcodes/instrument_drivers/Keysight/N9030B.py index 2d4ba19b2ed0..c619e4aa14dc 100644 --- a/qcodes/instrument_drivers/Keysight/N9030B.py +++ b/qcodes/instrument_drivers/Keysight/N9030B.py @@ -292,9 +292,9 @@ def __init__(self, parent: "N9030B", name: str, *arg: Any, **kwargs: Any): "513": 13799999995, "526": 26999999995} opt: str - for x in self._valid_max_freq.keys(): - if x in self._options(): - opt = x + for freq in self._valid_max_freq.keys(): + if freq in self._options(): + opt = freq self._max_freq = self._valid_max_freq[opt] self.add_parameter( From 28704bfe3f41bd01c1b6e89175c638810f005a55 Mon Sep 17 00:00:00 2001 From: Akshita <36688304+Akshita07@users.noreply.github.com> Date: Sun, 17 Jan 2021 10:52:01 +0100 Subject: [PATCH 30/81] Rename n to number Co-authored-by: Trevor Morgan <63689909+trevormorgan@users.noreply.github.com> --- qcodes/instrument_drivers/Keysight/N9030B.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qcodes/instrument_drivers/Keysight/N9030B.py b/qcodes/instrument_drivers/Keysight/N9030B.py index c619e4aa14dc..326be80b4207 100644 --- a/qcodes/instrument_drivers/Keysight/N9030B.py +++ b/qcodes/instrument_drivers/Keysight/N9030B.py @@ -28,7 +28,7 @@ def __init__(self, number: int, *args: Any, **kwargs: Any) -> None: self.instrument: Union["SpectrumAnalyzer", "PhaseNoise"] self.root_instrument: "N9030B" - self.n = number + self.number = number def get_raw(self) -> ParamRawDataType: return self.instrument._get_data(trace_num=self.n) From 8641451b3f056b2cdfa75c7cb4564c0e2f98e316 Mon Sep 17 00:00:00 2001 From: Akshita07 Date: Sun, 17 Jan 2021 11:15:21 +0100 Subject: [PATCH 31/81] fixes according to review --- qcodes/instrument/sims/Keysight_N9030B.yaml | 4 ++-- qcodes/instrument_drivers/Keysight/N9030B.py | 22 ++++++++++---------- qcodes/tests/drivers/test_keysight_n9030b.py | 4 ++-- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/qcodes/instrument/sims/Keysight_N9030B.yaml b/qcodes/instrument/sims/Keysight_N9030B.yaml index 9602ee984ea8..ba54080f7e46 100644 --- a/qcodes/instrument/sims/Keysight_N9030B.yaml +++ b/qcodes/instrument/sims/Keysight_N9030B.yaml @@ -44,7 +44,7 @@ devices: q: ":INITiate:CONTinuous {}" channels: - SA: + sa: dialogues: - q: ":SENS:FREQuency:TUNE:IMMediate" - q: ":READ:SANalyzer1?" @@ -152,7 +152,7 @@ devices: type: str valid: ["FFT", "SWE"] - PNoise: + pn: dialogues: - q: ":SENSe:FREQuency:CARRier:SEARch" - q: ":READ:LPLot1?" diff --git a/qcodes/instrument_drivers/Keysight/N9030B.py b/qcodes/instrument_drivers/Keysight/N9030B.py index 326be80b4207..2aa57a5ee8a6 100644 --- a/qcodes/instrument_drivers/Keysight/N9030B.py +++ b/qcodes/instrument_drivers/Keysight/N9030B.py @@ -25,13 +25,13 @@ class Trace(ParameterWithSetpoints): def __init__(self, number: int, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) - self.instrument: Union["SpectrumAnalyzer", "PhaseNoise"] + self.instrument: Union["SpectrumAnalyzerMode", "PhaseNoiseMode"] self.root_instrument: "N9030B" self.number = number def get_raw(self) -> ParamRawDataType: - return self.instrument._get_data(trace_num=self.n) + return self.instrument._get_data(trace_num=self.number) class SpectrumAnalyzerMode(InstrumentChannel): @@ -292,9 +292,9 @@ def __init__(self, parent: "N9030B", name: str, *arg: Any, **kwargs: Any): "513": 13799999995, "526": 26999999995} opt: str - for freq in self._valid_max_freq.keys(): - if freq in self._options(): - opt = freq + for hw_opt_for_max_freq in self._valid_max_freq.keys(): + if hw_opt_for_max_freq in self._options(): + opt = hw_opt_for_max_freq self._max_freq = self._valid_max_freq[opt] self.add_parameter( @@ -432,8 +432,8 @@ class N9030B(VisaInstrument): Driver for Keysight N9030B PXA signal analyzer. """ - CHANNEL_CLASS_1 = SpectrumAnalyzer - CHANNEL_CLASS_2 = PhaseNoise + CHANNEL_CLASS_1 = SpectrumAnalyzerMode + CHANNEL_CLASS_2 = PhaseNoiseMode def __init__(self, name: str, address: str, **kwargs: Any) -> None: super().__init__(name, address, terminator='\n', **kwargs) @@ -484,18 +484,18 @@ def __init__(self, name: str, address: str, **kwargs: Any) -> None: if "SA" in self._available_modes(): sa_mode = ChannelList( - self, "SA", self.CHANNEL_CLASS_1, snapshotable=True + self, "sa", self.CHANNEL_CLASS_1, snapshotable=True ) - self.add_submodule("SA", sa_mode) + self.add_submodule("sa", sa_mode) else: self.log.info("Spectrum Analyzer mode is not available on this " "instrument.") if "PNOISE" in self._available_modes(): pnoise_mode = ChannelList( - self, "PNoise", self.CHANNEL_CLASS_2, snapshotable=True + self, "pn", self.CHANNEL_CLASS_2, snapshotable=True ) - self.add_submodule("PNoise", pnoise_mode) + self.add_submodule("pn", pnoise_mode) else: self.log.info("Phase Noise mode is not available on this " "instrument.") diff --git a/qcodes/tests/drivers/test_keysight_n9030b.py b/qcodes/tests/drivers/test_keysight_n9030b.py index 3b180722ce78..7eecd11779e3 100644 --- a/qcodes/tests/drivers/test_keysight_n9030b.py +++ b/qcodes/tests/drivers/test_keysight_n9030b.py @@ -17,12 +17,12 @@ def _make_driver(): @pytest.fixture(name="sa") def _activate_sa_mode(driver): - sa = driver.SA + sa = driver.sa yield sa @pytest.fixture(name="pnoise") def _activate_pnoise_mode(driver): - pn = driver.PNoise + pn = driver.pn yield pn From a162d0a57cbaafea54e0bb1dcdf7fab0f0467d7d Mon Sep 17 00:00:00 2001 From: Akshita07 Date: Sun, 17 Jan 2021 11:23:51 +0100 Subject: [PATCH 32/81] Add set up methods for both modes --- qcodes/instrument_drivers/Keysight/N9030B.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/qcodes/instrument_drivers/Keysight/N9030B.py b/qcodes/instrument_drivers/Keysight/N9030B.py index 2aa57a5ee8a6..2a4fe767eb34 100644 --- a/qcodes/instrument_drivers/Keysight/N9030B.py +++ b/qcodes/instrument_drivers/Keysight/N9030B.py @@ -41,8 +41,6 @@ class SpectrumAnalyzerMode(InstrumentChannel): def __init__(self, parent: "N9030B", name: str, *arg: Any, **kwargs: Any): super().__init__(parent, name, *arg, **kwargs) - self.mode("SA") - self._min_freq = 2 self._max_freq = 50e9 @@ -252,6 +250,12 @@ def update_trace(self) -> None: self.start() self.stop() + def setup(self) -> None: + """ + Sets up Spectrum Analyzer mode for the instrument. + """ + self.mode("SA") + def setup_swept_sa_sweep(self, start: float, stop: float, @@ -284,8 +288,6 @@ class PhaseNoiseMode(InstrumentChannel): def __init__(self, parent: "N9030B", name: str, *arg: Any, **kwargs: Any): super().__init__(parent, name, *arg, **kwargs) - self.mode("PNOISE") - self._min_freq = 1 self._valid_max_freq: Dict[str, float] = {"503": 3699999995, "508": 8499999995, @@ -401,6 +403,12 @@ def _get_data(self, trace_num: int) -> ParamRawDataType: return data + def setup(self) -> None: + """ + Sets up Phase Noise mode for the instrument. + """ + self.mode("PNOISE") + def setup_log_plot_sweep(self, start_offset: float, stop_offset: float, @@ -499,7 +507,6 @@ def __init__(self, name: str, address: str, **kwargs: Any) -> None: else: self.log.info("Phase Noise mode is not available on this " "instrument.") - self.reset() self.connect_message() def _available_modes(self) -> Tuple[str, ...]: From b04c24a24b21c4196f0841e0a77953ff0986de0c Mon Sep 17 00:00:00 2001 From: Akshita07 Date: Sun, 17 Jan 2021 13:26:57 +0100 Subject: [PATCH 33/81] Add tests for measurements setup --- qcodes/instrument_drivers/Keysight/N9030B.py | 20 +++------- qcodes/tests/drivers/test_keysight_n9030b.py | 40 ++++++++++++++------ 2 files changed, 34 insertions(+), 26 deletions(-) diff --git a/qcodes/instrument_drivers/Keysight/N9030B.py b/qcodes/instrument_drivers/Keysight/N9030B.py index 2a4fe767eb34..50bbd9529fd7 100644 --- a/qcodes/instrument_drivers/Keysight/N9030B.py +++ b/qcodes/instrument_drivers/Keysight/N9030B.py @@ -250,12 +250,6 @@ def update_trace(self) -> None: self.start() self.stop() - def setup(self) -> None: - """ - Sets up Spectrum Analyzer mode for the instrument. - """ - self.mode("SA") - def setup_swept_sa_sweep(self, start: float, stop: float, @@ -263,6 +257,7 @@ def setup_swept_sa_sweep(self, """ Sets up the Swept SA measurement sweep for Spectrum Analyzer Mode. """ + self.mode("SA") if "SANalyzer" in self._available_meas(): self.measurement("SANalyzer") else: @@ -403,12 +398,6 @@ def _get_data(self, trace_num: int) -> ParamRawDataType: return data - def setup(self) -> None: - """ - Sets up Phase Noise mode for the instrument. - """ - self.mode("PNOISE") - def setup_log_plot_sweep(self, start_offset: float, stop_offset: float, @@ -417,6 +406,7 @@ def setup_log_plot_sweep(self, """ Sets up the Log Plot measurement sweep for Phase Noise Mode. """ + self.mode("PNOISE") if "LPLot" in self._available_meas(): self.measurement("LPLot") else: @@ -491,8 +481,9 @@ def __init__(self, name: str, address: str, **kwargs: Any) -> None: ) if "SA" in self._available_modes(): + swept_sa = SpectrumAnalyzerMode(self, name="swept_sa") sa_mode = ChannelList( - self, "sa", self.CHANNEL_CLASS_1, snapshotable=True + self, "sa", self.CHANNEL_CLASS_1, [swept_sa], snapshotable=True ) self.add_submodule("sa", sa_mode) else: @@ -500,8 +491,9 @@ def __init__(self, name: str, address: str, **kwargs: Any) -> None: "instrument.") if "PNOISE" in self._available_modes(): + log_plot = PhaseNoiseMode(self, name="log_plot") pnoise_mode = ChannelList( - self, "pn", self.CHANNEL_CLASS_2, snapshotable=True + self, "pn", self.CHANNEL_CLASS_2, [log_plot], snapshotable=True ) self.add_submodule("pn", pnoise_mode) else: diff --git a/qcodes/tests/drivers/test_keysight_n9030b.py b/qcodes/tests/drivers/test_keysight_n9030b.py index 7eecd11779e3..00a9e5b70af3 100644 --- a/qcodes/tests/drivers/test_keysight_n9030b.py +++ b/qcodes/tests/drivers/test_keysight_n9030b.py @@ -1,6 +1,8 @@ import pytest -import qcodes.instrument_drivers.Keysight.N9030B as N9030B +from qcodes.instrument_drivers.Keysight.N9030B import (N9030B, + SpectrumAnalyzerMode, + PhaseNoiseMode) import qcodes.instrument.sims as sims VISALIB = sims.__file__.replace('__init__.py', 'Keysight_N9030B.yaml@sim') @@ -8,22 +10,18 @@ @pytest.fixture(name="driver") def _make_driver(): - driver = N9030B.N9030B('n9030B_sim', - address="GPIB::1::INSTR", - visalib=VISALIB) + driver = N9030B('n9030B_sim', address="GPIB::1::INSTR", visalib=VISALIB) yield driver driver.close() -@pytest.fixture(name="sa") -def _activate_sa_mode(driver): - sa = driver.sa - yield sa +@pytest.fixture(name="swept_sa") +def _activate_swept_sa_measurement(driver): + yield driver.sa.swept_sa -@pytest.fixture(name="pnoise") -def _activate_pnoise_mode(driver): - pn = driver.pn - yield pn +@pytest.fixture(name="pn") +def _activate_log_plot_measurement(driver): + yield driver.pn.log_plot def test_idn(driver): @@ -31,3 +29,21 @@ def test_idn(driver): 'model': 'N9030B', 'serial': '1000', 'vendor': 'Keysight Technologies'} == driver.IDN() + + +def test_swept_sa_setup(swept_sa): + assert isinstance(swept_sa, SpectrumAnalyzerMode) + + swept_sa.setup_swept_sa_sweep(1e3, 1e6, 501) + assert swept_sa.start() == 1e3 + assert swept_sa.stop() == 1e6 + assert swept_sa.npts() == 501 + + +def test_log_plot_setup(log_plot): + assert isinstance(log_plot, PhaseNoiseMode) + + log_plot.setup_log_plot_sweep(100, 1000, 10001) + assert log_plot.start_offset() == 100 + assert log_plot.stop_offset() == 1000 + assert log_plot.npts() == 10001 From f51eab94e5b0976cb5467378a3e6f92a288e18ff Mon Sep 17 00:00:00 2001 From: Akshita07 Date: Mon, 18 Jan 2021 11:11:35 +0100 Subject: [PATCH 34/81] Update yaml --- qcodes/instrument/sims/Keysight_N9030B.yaml | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/qcodes/instrument/sims/Keysight_N9030B.yaml b/qcodes/instrument/sims/Keysight_N9030B.yaml index ba54080f7e46..5c346b788be7 100644 --- a/qcodes/instrument/sims/Keysight_N9030B.yaml +++ b/qcodes/instrument/sims/Keysight_N9030B.yaml @@ -52,27 +52,23 @@ devices: properties: start: - default: 2 + default: 100 getter: q: ":SENSe:FREQuency:STARt?" r: "{}" setter: q: ":SENSe:FREQuency:STARt {}" specs: - min: 2 - max: 49999999990 type: float stop: - default: 12 + default: 1000 getter: q: ":SENSe:FREQuency:STOP?" r: "{}" setter: q: ":SENSe:FREQuency:STOP {}" specs: - min: 12 - max: 50e9 type: float center: @@ -83,8 +79,6 @@ devices: setter: q: ":SENSe:FREQuency:CENTer {}" specs: - min: 7 - max: 49999999995 type: float span: @@ -95,8 +89,6 @@ devices: setter: q: ":SENSe:FREQuency:SPAN {}" specs: - min: 10 - max: 49999999998 type: float npts: From b7cee88d9fa2b0142aafab9f8e897612dc738fe4 Mon Sep 17 00:00:00 2001 From: Akshita07 Date: Mon, 18 Jan 2021 11:12:21 +0100 Subject: [PATCH 35/81] Add min max freq for sa mode and fix calls to drivers params --- qcodes/instrument_drivers/Keysight/N9030B.py | 49 +++++++++++++------- 1 file changed, 31 insertions(+), 18 deletions(-) diff --git a/qcodes/instrument_drivers/Keysight/N9030B.py b/qcodes/instrument_drivers/Keysight/N9030B.py index 50bbd9529fd7..b9d40f58c367 100644 --- a/qcodes/instrument_drivers/Keysight/N9030B.py +++ b/qcodes/instrument_drivers/Keysight/N9030B.py @@ -41,8 +41,17 @@ class SpectrumAnalyzerMode(InstrumentChannel): def __init__(self, parent: "N9030B", name: str, *arg: Any, **kwargs: Any): super().__init__(parent, name, *arg, **kwargs) - self._min_freq = 2 - self._max_freq = 50e9 + self._min_freq = -8e7 + self._valid_max_freq: Dict[str, float] = {"503": 3.7e9, + "508": 8.5e9, + "513": 13.8e9, + "526": 27e9, + "544": 44.5e9} + opt: str + for hw_opt_for_max_freq in self._valid_max_freq.keys(): + if hw_opt_for_max_freq in self.root_instrument._options(): + opt = hw_opt_for_max_freq + self._max_freq = self._valid_max_freq[opt] self.add_parameter( name="start", @@ -231,14 +240,16 @@ def _get_data(self, trace_num: int) -> ParamRawDataType: """ Gets data from the measurement. """ - self.cont_meas("OFF") + self.root_instrument.cont_meas("OFF") try: - timeout = self.sweep_time() + self._additional_wait + timeout = self.root_instrument.sweep_time() + \ + self.root_instrument._additional_wait with self.timeout.set_to(timeout): - data_str = self.ask(f":READ:{self.measurement}{trace_num}?") + data_str = self.ask(f":READ:{self.root_instrument.measurement}" + f"{trace_num}?") data = np.array(data_str.rstrip()).astype("float64") finally: - self.cont_meas("ON") + self.root_instrument.cont_meas("ON") return data @@ -257,9 +268,9 @@ def setup_swept_sa_sweep(self, """ Sets up the Swept SA measurement sweep for Spectrum Analyzer Mode. """ - self.mode("SA") - if "SANalyzer" in self._available_meas(): - self.measurement("SANalyzer") + self.root_instrument.mode("SA") + if "SANalyzer" in self.root_instrument._available_meas(): + self.root_instrument.measurement("SANalyzer") else: raise RuntimeError("Swept SA measurement is not available on your " "Keysight N9030B instrument with Spectrum " @@ -287,10 +298,11 @@ def __init__(self, parent: "N9030B", name: str, *arg: Any, **kwargs: Any): self._valid_max_freq: Dict[str, float] = {"503": 3699999995, "508": 8499999995, "513": 13799999995, - "526": 26999999995} + "526": 26999999995, + "544": 44499999995} opt: str for hw_opt_for_max_freq in self._valid_max_freq.keys(): - if hw_opt_for_max_freq in self._options(): + if hw_opt_for_max_freq in self.root_instrument._options(): opt = hw_opt_for_max_freq self._max_freq = self._valid_max_freq[opt] @@ -317,7 +329,7 @@ def __init__(self, parent: "N9030B", name: str, *arg: Any, **kwargs: Any): get_cmd=":SENSe:LPLot:FREQuency:OFFSet:STOP?", set_cmd=self._set_stop_offset, get_parser=float, - vals=Numbers(self._min_freq + 100, self._max_freq), + vals=Numbers(self._min_freq + 99, self._max_freq), docstring="stop frequency offset for the plot" ) @@ -389,12 +401,13 @@ def _get_data(self, trace_num: int) -> ParamRawDataType: """ Gets data from the measurement. """ - self.cont_meas("OFF") + self.root_instrument.cont_meas("OFF") try: - data_str = self.ask(f":READ:{self.measurement}{trace_num}?") + data_str = self.ask(f":READ:{self.root_instrument.measurement}" + f"{trace_num}?") data = np.array(data_str.rstrip()).astype("float64") finally: - self.cont_meas("ON") + self.root_instrument.cont_meas("ON") return data @@ -406,9 +419,9 @@ def setup_log_plot_sweep(self, """ Sets up the Log Plot measurement sweep for Phase Noise Mode. """ - self.mode("PNOISE") - if "LPLot" in self._available_meas(): - self.measurement("LPLot") + self.root_instrument.mode("PNOISE") + if "LPLot" in self.root_instrument._available_meas(): + self.root_instrument.measurement("LPLot") else: raise RuntimeError("Log Plot measurement is not available on your " "Keysight N9030B instrument with Phase Noise " From 568b35060450ff9fbd8bcbbc2661ee8d8c83abf3 Mon Sep 17 00:00:00 2001 From: Akshita07 Date: Mon, 18 Jan 2021 11:12:45 +0100 Subject: [PATCH 36/81] pn to log_plot fixture name --- qcodes/tests/drivers/test_keysight_n9030b.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qcodes/tests/drivers/test_keysight_n9030b.py b/qcodes/tests/drivers/test_keysight_n9030b.py index 00a9e5b70af3..dd37dbd6aca0 100644 --- a/qcodes/tests/drivers/test_keysight_n9030b.py +++ b/qcodes/tests/drivers/test_keysight_n9030b.py @@ -19,7 +19,7 @@ def _make_driver(): def _activate_swept_sa_measurement(driver): yield driver.sa.swept_sa -@pytest.fixture(name="pn") +@pytest.fixture(name="log_plot") def _activate_log_plot_measurement(driver): yield driver.pn.log_plot From 2b0a98549d3234f9380b33d4d731495dd0d7db12 Mon Sep 17 00:00:00 2001 From: Akshita07 Date: Mon, 18 Jan 2021 12:10:55 +0100 Subject: [PATCH 37/81] Fix string parsing for catalog commands --- qcodes/instrument/sims/Keysight_N9030B.yaml | 2 +- qcodes/instrument_drivers/Keysight/N9030B.py | 19 +++++++++++-------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/qcodes/instrument/sims/Keysight_N9030B.yaml b/qcodes/instrument/sims/Keysight_N9030B.yaml index 5c346b788be7..187c5bd9a67c 100644 --- a/qcodes/instrument/sims/Keysight_N9030B.yaml +++ b/qcodes/instrument/sims/Keysight_N9030B.yaml @@ -12,7 +12,7 @@ devices: - q: ":INSTrument:CATalog?" r: "SA,PNOISE" - q: ":CONFigure:CATalog?" - r: "SANalyzer,LPLot" + r: "SAN,LPL" - q: "*OPT?" r: "526" - q: "*IDN?" diff --git a/qcodes/instrument_drivers/Keysight/N9030B.py b/qcodes/instrument_drivers/Keysight/N9030B.py index b9d40f58c367..ecf2352a5d83 100644 --- a/qcodes/instrument_drivers/Keysight/N9030B.py +++ b/qcodes/instrument_drivers/Keysight/N9030B.py @@ -242,9 +242,8 @@ def _get_data(self, trace_num: int) -> ParamRawDataType: """ self.root_instrument.cont_meas("OFF") try: - timeout = self.root_instrument.sweep_time() + \ - self.root_instrument._additional_wait - with self.timeout.set_to(timeout): + timeout = self.sweep_time() + self.root_instrument._additional_wait + with self.root_instrument.timeout.set_to(timeout): data_str = self.ask(f":READ:{self.root_instrument.measurement}" f"{trace_num}?") data = np.array(data_str.rstrip()).astype("float64") @@ -269,7 +268,7 @@ def setup_swept_sa_sweep(self, Sets up the Swept SA measurement sweep for Spectrum Analyzer Mode. """ self.root_instrument.mode("SA") - if "SANalyzer" in self.root_instrument._available_meas(): + if "SAN" in self.root_instrument._available_meas(): self.root_instrument.measurement("SANalyzer") else: raise RuntimeError("Swept SA measurement is not available on your " @@ -420,7 +419,7 @@ def setup_log_plot_sweep(self, Sets up the Log Plot measurement sweep for Phase Noise Mode. """ self.root_instrument.mode("PNOISE") - if "LPLot" in self.root_instrument._available_meas(): + if "LPL" in self.root_instrument._available_meas(): self.root_instrument.measurement("LPLot") else: raise RuntimeError("Log Plot measurement is not available on your " @@ -519,14 +518,18 @@ def _available_modes(self) -> Tuple[str, ...]: Returns present and licensed modes for the instrument. """ available_modes = self.ask(":INSTrument:CATalog?") - return tuple(available_modes.split(',')) + av_modes = available_modes[1:-1].split(',') + modes = () + for i in range(len(av_modes)): + modes = modes + (av_modes[i].split(' ')[0],) + return modes def _available_meas(self) -> Tuple[str, ...]: """ Gives available measurement with a given mode for the instrument """ available_meas = self.ask(":CONFigure:CATalog?") - return tuple(available_meas.split(',')) + return tuple(available_meas[1:-1].split(',')) def _enable_cont_meas(self, val: str) -> None: """ @@ -539,7 +542,7 @@ def _options(self) -> Tuple[str, ...]: Returns installed options numbers. """ options_raw = self.ask('*OPT?') - return tuple(options_raw.split(',')) + return tuple(options_raw[1:-1].split(',')) def reset(self) -> None: """ From 0a175ac4701a7d260f343611cc4cd1bebcc246ad Mon Sep 17 00:00:00 2001 From: Akshita07 Date: Mon, 18 Jan 2021 12:15:42 +0100 Subject: [PATCH 38/81] minor fix for parsing --- qcodes/instrument_drivers/Keysight/N9030B.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qcodes/instrument_drivers/Keysight/N9030B.py b/qcodes/instrument_drivers/Keysight/N9030B.py index ecf2352a5d83..150449008baf 100644 --- a/qcodes/instrument_drivers/Keysight/N9030B.py +++ b/qcodes/instrument_drivers/Keysight/N9030B.py @@ -269,7 +269,7 @@ def setup_swept_sa_sweep(self, """ self.root_instrument.mode("SA") if "SAN" in self.root_instrument._available_meas(): - self.root_instrument.measurement("SANalyzer") + self.root_instrument.measurement("SAN") else: raise RuntimeError("Swept SA measurement is not available on your " "Keysight N9030B instrument with Spectrum " @@ -420,7 +420,7 @@ def setup_log_plot_sweep(self, """ self.root_instrument.mode("PNOISE") if "LPL" in self.root_instrument._available_meas(): - self.root_instrument.measurement("LPLot") + self.root_instrument.measurement("LPL") else: raise RuntimeError("Log Plot measurement is not available on your " "Keysight N9030B instrument with Phase Noise " From f5a0cdddbf33e2312eda96e46c240cec5537cb37 Mon Sep 17 00:00:00 2001 From: Akshita07 Date: Mon, 18 Jan 2021 12:27:09 +0100 Subject: [PATCH 39/81] Add set cmd to sweep time --- qcodes/instrument_drivers/Keysight/N9030B.py | 1 + 1 file changed, 1 insertion(+) diff --git a/qcodes/instrument_drivers/Keysight/N9030B.py b/qcodes/instrument_drivers/Keysight/N9030B.py index 150449008baf..000b83e5c9ec 100644 --- a/qcodes/instrument_drivers/Keysight/N9030B.py +++ b/qcodes/instrument_drivers/Keysight/N9030B.py @@ -102,6 +102,7 @@ def __init__(self, parent: "N9030B", name: str, *arg: Any, **kwargs: Any): name="sweep_time", label="Sweep time", get_cmd=":SENSe:SWEep:TIME?", + set_cmd=":SENSe:SWEep:TIME {}", get_parser=float, unit="s", docstring="gets sweep time" From a036f4ff447cda8b09aad84814234d54ef0aadd2 Mon Sep 17 00:00:00 2001 From: Akshita07 Date: Mon, 18 Jan 2021 12:38:12 +0100 Subject: [PATCH 40/81] fix parsing for modes --- qcodes/instrument_drivers/Keysight/N9030B.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/qcodes/instrument_drivers/Keysight/N9030B.py b/qcodes/instrument_drivers/Keysight/N9030B.py index 000b83e5c9ec..7e73f98f0722 100644 --- a/qcodes/instrument_drivers/Keysight/N9030B.py +++ b/qcodes/instrument_drivers/Keysight/N9030B.py @@ -522,7 +522,10 @@ def _available_modes(self) -> Tuple[str, ...]: av_modes = available_modes[1:-1].split(',') modes = () for i in range(len(av_modes)): - modes = modes + (av_modes[i].split(' ')[0],) + if i == 0: + modes = modes + (av_modes[i].split(' ')[0],) + else: + modes = modes + (av_modes[i].split(' ')[1],) return modes def _available_meas(self) -> Tuple[str, ...]: From 01423438db61b244a41ea766b0c89bdb1cc4673a Mon Sep 17 00:00:00 2001 From: Akshita07 Date: Mon, 18 Jan 2021 12:41:56 +0100 Subject: [PATCH 41/81] update get_data methods --- qcodes/instrument_drivers/Keysight/N9030B.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/qcodes/instrument_drivers/Keysight/N9030B.py b/qcodes/instrument_drivers/Keysight/N9030B.py index 7e73f98f0722..f8a1a8aac131 100644 --- a/qcodes/instrument_drivers/Keysight/N9030B.py +++ b/qcodes/instrument_drivers/Keysight/N9030B.py @@ -241,15 +241,14 @@ def _get_data(self, trace_num: int) -> ParamRawDataType: """ Gets data from the measurement. """ - self.root_instrument.cont_meas("OFF") try: timeout = self.sweep_time() + self.root_instrument._additional_wait with self.root_instrument.timeout.set_to(timeout): data_str = self.ask(f":READ:{self.root_instrument.measurement}" f"{trace_num}?") data = np.array(data_str.rstrip()).astype("float64") - finally: - self.root_instrument.cont_meas("ON") + except TimeoutError: + pass return data @@ -401,13 +400,12 @@ def _get_data(self, trace_num: int) -> ParamRawDataType: """ Gets data from the measurement. """ - self.root_instrument.cont_meas("OFF") try: data_str = self.ask(f":READ:{self.root_instrument.measurement}" f"{trace_num}?") data = np.array(data_str.rstrip()).astype("float64") - finally: - self.root_instrument.cont_meas("ON") + except TimeoutError: + pass return data From f49e1cd000653977d800c407610b1bdc15d46780 Mon Sep 17 00:00:00 2001 From: Akshita07 Date: Mon, 18 Jan 2021 12:57:22 +0100 Subject: [PATCH 42/81] fix meas parsing --- qcodes/instrument_drivers/Keysight/N9030B.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/qcodes/instrument_drivers/Keysight/N9030B.py b/qcodes/instrument_drivers/Keysight/N9030B.py index f8a1a8aac131..da9988c62d06 100644 --- a/qcodes/instrument_drivers/Keysight/N9030B.py +++ b/qcodes/instrument_drivers/Keysight/N9030B.py @@ -531,7 +531,14 @@ def _available_meas(self) -> Tuple[str, ...]: Gives available measurement with a given mode for the instrument """ available_meas = self.ask(":CONFigure:CATalog?") - return tuple(available_meas[1:-1].split(',')) + av_meas = available_meas[1:-1].split(',') + meas = () + for i in range(len(av_meas)): + if i == 0: + meas = meas + (av_meas[i],) + else: + meas = meas + (av_meas[i][1:],) + return meas def _enable_cont_meas(self, val: str) -> None: """ From 31e7db566552953584fc8f501a568a1fa63c63e5 Mon Sep 17 00:00:00 2001 From: Akshita07 Date: Mon, 18 Jan 2021 13:38:20 +0100 Subject: [PATCH 43/81] update sample responses in yaml --- qcodes/instrument/sims/Keysight_N9030B.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/qcodes/instrument/sims/Keysight_N9030B.yaml b/qcodes/instrument/sims/Keysight_N9030B.yaml index 187c5bd9a67c..eebb1306a880 100644 --- a/qcodes/instrument/sims/Keysight_N9030B.yaml +++ b/qcodes/instrument/sims/Keysight_N9030B.yaml @@ -10,11 +10,11 @@ devices: - q: "*RST" - q: ":ABORt" - q: ":INSTrument:CATalog?" - r: "SA,PNOISE" + r: '"SA 1, PNOISE 14"' - q: ":CONFigure:CATalog?" - r: "SAN,LPL" + r: '"SAN, LPL"' - q: "*OPT?" - r: "526" + r: '"544"' - q: "*IDN?" r: "Keysight Technologies,N9030B,1000,0.1" From 9c1d4ad1ea63dea68ecd96b18c01937a99d251cc Mon Sep 17 00:00:00 2001 From: Akshita07 Date: Mon, 18 Jan 2021 13:43:01 +0100 Subject: [PATCH 44/81] fix get_data queries and meas valid vals --- qcodes/instrument_drivers/Keysight/N9030B.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/qcodes/instrument_drivers/Keysight/N9030B.py b/qcodes/instrument_drivers/Keysight/N9030B.py index da9988c62d06..719f08d31909 100644 --- a/qcodes/instrument_drivers/Keysight/N9030B.py +++ b/qcodes/instrument_drivers/Keysight/N9030B.py @@ -244,7 +244,8 @@ def _get_data(self, trace_num: int) -> ParamRawDataType: try: timeout = self.sweep_time() + self.root_instrument._additional_wait with self.root_instrument.timeout.set_to(timeout): - data_str = self.ask(f":READ:{self.root_instrument.measurement}" + data_str = self.ask(f":READ:" + f"{self.root_instrument.measurement()}" f"{trace_num}?") data = np.array(data_str.rstrip()).astype("float64") except TimeoutError: @@ -401,7 +402,7 @@ def _get_data(self, trace_num: int) -> ParamRawDataType: Gets data from the measurement. """ try: - data_str = self.ask(f":READ:{self.root_instrument.measurement}" + data_str = self.ask(f":READ:{self.root_instrument.measurement()}" f"{trace_num}?") data = np.array(data_str.rstrip()).astype("float64") except TimeoutError: @@ -464,7 +465,7 @@ def __init__(self, name: str, address: str, **kwargs: Any) -> None: name="measurement", get_cmd=":CONFigure?", set_cmd=":CONFigure:{}", - vals=Enum(*self._available_meas()), + vals=Enum("SAN", "LPL"), docstring="Sets measurement type from among the available " "measurement types." ) From 368f7efcea1bab95dbff3aeaa1542e4b2c08d0e3 Mon Sep 17 00:00:00 2001 From: Akshita07 Date: Mon, 18 Jan 2021 14:16:06 +0100 Subject: [PATCH 45/81] add docstring and mypy fixes --- qcodes/instrument_drivers/Keysight/N9030B.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/qcodes/instrument_drivers/Keysight/N9030B.py b/qcodes/instrument_drivers/Keysight/N9030B.py index 719f08d31909..1af7d1f3bf2d 100644 --- a/qcodes/instrument_drivers/Keysight/N9030B.py +++ b/qcodes/instrument_drivers/Keysight/N9030B.py @@ -439,7 +439,15 @@ def autotune(self) -> None: class N9030B(VisaInstrument): """ - Driver for Keysight N9030B PXA signal analyzer. + Driver for Keysight N9030B PXA signal analyzer. Keysight N9030B PXA + siganl analyzer is part of Keysight X-Series Multi-touch Signal + Analyzers. + This driver allows Swept SA measurements in Spectrum Analyzer mode and + Log Plot measurements in Phase Noise mode of the instrument. + + Args: + name + address """ CHANNEL_CLASS_1 = SpectrumAnalyzerMode @@ -519,7 +527,7 @@ def _available_modes(self) -> Tuple[str, ...]: """ available_modes = self.ask(":INSTrument:CATalog?") av_modes = available_modes[1:-1].split(',') - modes = () + modes: Tuple[str, ...] = () for i in range(len(av_modes)): if i == 0: modes = modes + (av_modes[i].split(' ')[0],) @@ -533,7 +541,7 @@ def _available_meas(self) -> Tuple[str, ...]: """ available_meas = self.ask(":CONFigure:CATalog?") av_meas = available_meas[1:-1].split(',') - meas = () + meas: Tuple[str, ...] = () for i in range(len(av_meas)): if i == 0: meas = meas + (av_meas[i],) From 89b753bd9a9620278fa23a7d520514af6294145c Mon Sep 17 00:00:00 2001 From: Akshita07 Date: Mon, 18 Jan 2021 14:30:56 +0100 Subject: [PATCH 46/81] Use enumerate inplace of range len --- qcodes/instrument_drivers/Keysight/N9030B.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qcodes/instrument_drivers/Keysight/N9030B.py b/qcodes/instrument_drivers/Keysight/N9030B.py index 1af7d1f3bf2d..d4e8478f8da5 100644 --- a/qcodes/instrument_drivers/Keysight/N9030B.py +++ b/qcodes/instrument_drivers/Keysight/N9030B.py @@ -528,7 +528,7 @@ def _available_modes(self) -> Tuple[str, ...]: available_modes = self.ask(":INSTrument:CATalog?") av_modes = available_modes[1:-1].split(',') modes: Tuple[str, ...] = () - for i in range(len(av_modes)): + for i, _ in enumerate(av_modes): if i == 0: modes = modes + (av_modes[i].split(' ')[0],) else: @@ -542,7 +542,7 @@ def _available_meas(self) -> Tuple[str, ...]: available_meas = self.ask(":CONFigure:CATalog?") av_meas = available_meas[1:-1].split(',') meas: Tuple[str, ...] = () - for i in range(len(av_meas)): + for i, _ in enumerate(av_meas): if i == 0: meas = meas + (av_meas[i],) else: From 68a0cefbb46de625a25079272b2cafcddd4d1c19 Mon Sep 17 00:00:00 2001 From: Akshita07 Date: Mon, 18 Jan 2021 14:44:28 +0100 Subject: [PATCH 47/81] update notebook --- .../Qcodes example with Keysight N9030B.ipynb | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/examples/driver_examples/Qcodes example with Keysight N9030B.ipynb b/docs/examples/driver_examples/Qcodes example with Keysight N9030B.ipynb index d20658da8fe3..8a1869c036df 100644 --- a/docs/examples/driver_examples/Qcodes example with Keysight N9030B.ipynb +++ b/docs/examples/driver_examples/Qcodes example with Keysight N9030B.ipynb @@ -31,7 +31,7 @@ "from qcodes.dataset import initialise_or_create_database_at, load_or_create_experiment, Measurement\n", "from qcodes.dataset.plotting import plot_dataset, plot_by_id\n", "\n", - "from qcodes.instrument_drivers.Keysight.N9030B import N9030B, SpectrumAnalyzer, PhaseNoise" + "from qcodes.instrument_drivers.Keysight.N9030B import N9030B" ] }, { @@ -40,7 +40,7 @@ "metadata": {}, "outputs": [], "source": [ - "instr = N9030B(\"n9030b\",\"GPIB_Address\")" + "driver = N9030B(\"n9030b\",\"GPIB_Address\")" ] }, { @@ -49,7 +49,7 @@ "metadata": {}, "outputs": [], "source": [ - "instr.IDN()" + "driver.IDN()" ] }, { @@ -65,7 +65,7 @@ "metadata": {}, "outputs": [], "source": [ - "sa = instr.SA()" + "swept_sa = driver.sa.swept_sa" ] }, { @@ -74,7 +74,7 @@ "metadata": {}, "outputs": [], "source": [ - "sa.setup_swept_sa_sweep(start=200, stop= 10e3, npts=20001)" + "swept_sa.setup_swept_sa_sweep(start=200, stop= 10e3, npts=20001)" ] }, { @@ -109,7 +109,7 @@ "outputs": [], "source": [ "meas1 = Measurement()\n", - "meas1.register_parameter(sa.trace)" + "meas1.register_parameter(swept_sa.trace)" ] }, { @@ -119,7 +119,7 @@ "outputs": [], "source": [ "with meas1.run() as datasaver:\n", - " datasaver.add_result((sa.trace, sa.trace.get()))\n", + " datasaver.add_result((swept_sa.trace, swept_sa.trace.get()))\n", "\n", "dataset = datasaver.dataset" ] @@ -153,7 +153,7 @@ "metadata": {}, "outputs": [], "source": [ - "pn = instr.PNoise()" + "log_plot = driver.pn.log_plot" ] }, { @@ -162,7 +162,7 @@ "metadata": {}, "outputs": [], "source": [ - "pn.setup_log_plot_sweep(start_offset=10, stop_offset=200, npts=1001)" + "log_plot.setup_log_plot_sweep(start_offset=10, stop_offset=200, npts=1001)" ] }, { @@ -179,7 +179,7 @@ "outputs": [], "source": [ "meas2 = Measurement()\n", - "meas2.register_parameter(pn.trace)" + "meas2.register_parameter(log_plot.trace)" ] }, { @@ -189,7 +189,7 @@ "outputs": [], "source": [ "with meas1.run() as datasaver:\n", - " datasaver.add_result((pn.trace, pn.trace.get()))\n", + " datasaver.add_result((log_plot.trace, log_plot.trace.get()))\n", "\n", "run_id = datasaver.run_id" ] From 601629e96dc89aa7d83a3090ac511ce3110272a8 Mon Sep 17 00:00:00 2001 From: Akshita07 Date: Mon, 18 Jan 2021 20:32:05 +0100 Subject: [PATCH 48/81] split data string --- qcodes/instrument_drivers/Keysight/N9030B.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qcodes/instrument_drivers/Keysight/N9030B.py b/qcodes/instrument_drivers/Keysight/N9030B.py index d4e8478f8da5..7db025609d9d 100644 --- a/qcodes/instrument_drivers/Keysight/N9030B.py +++ b/qcodes/instrument_drivers/Keysight/N9030B.py @@ -247,7 +247,7 @@ def _get_data(self, trace_num: int) -> ParamRawDataType: data_str = self.ask(f":READ:" f"{self.root_instrument.measurement()}" f"{trace_num}?") - data = np.array(data_str.rstrip()).astype("float64") + data = np.array(data_str.rstrip().split(",")).astype("float64") except TimeoutError: pass @@ -404,7 +404,7 @@ def _get_data(self, trace_num: int) -> ParamRawDataType: try: data_str = self.ask(f":READ:{self.root_instrument.measurement()}" f"{trace_num}?") - data = np.array(data_str.rstrip()).astype("float64") + data = np.array(data_str.rstrip().split(",")).astype("float64") except TimeoutError: pass From 60286379381cfd72247ac1269fdeac4b187d2727 Mon Sep 17 00:00:00 2001 From: Akshita07 Date: Mon, 18 Jan 2021 21:11:06 +0100 Subject: [PATCH 49/81] get only trace data --- qcodes/instrument_drivers/Keysight/N9030B.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/qcodes/instrument_drivers/Keysight/N9030B.py b/qcodes/instrument_drivers/Keysight/N9030B.py index 7db025609d9d..3a8512b426c7 100644 --- a/qcodes/instrument_drivers/Keysight/N9030B.py +++ b/qcodes/instrument_drivers/Keysight/N9030B.py @@ -251,7 +251,8 @@ def _get_data(self, trace_num: int) -> ParamRawDataType: except TimeoutError: pass - return data + trace_data = data[0::2] + return trace_data def update_trace(self) -> None: """ @@ -408,7 +409,8 @@ def _get_data(self, trace_num: int) -> ParamRawDataType: except TimeoutError: pass - return data + trace_data = data[0::2] + return trace_data def setup_log_plot_sweep(self, start_offset: float, From 990401744a94c853c6eb927e385dabba7e0b8a06 Mon Sep 17 00:00:00 2001 From: Akshita07 Date: Mon, 18 Jan 2021 21:14:44 +0100 Subject: [PATCH 50/81] take y vals as trace data --- qcodes/instrument_drivers/Keysight/N9030B.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qcodes/instrument_drivers/Keysight/N9030B.py b/qcodes/instrument_drivers/Keysight/N9030B.py index 3a8512b426c7..86155fe22584 100644 --- a/qcodes/instrument_drivers/Keysight/N9030B.py +++ b/qcodes/instrument_drivers/Keysight/N9030B.py @@ -251,7 +251,7 @@ def _get_data(self, trace_num: int) -> ParamRawDataType: except TimeoutError: pass - trace_data = data[0::2] + trace_data = data[1::2] return trace_data def update_trace(self) -> None: @@ -409,7 +409,7 @@ def _get_data(self, trace_num: int) -> ParamRawDataType: except TimeoutError: pass - trace_data = data[0::2] + trace_data = data[1::2] return trace_data def setup_log_plot_sweep(self, From 33fd0d9cc40b181ba4ef0beaa4c592e45122e9b5 Mon Sep 17 00:00:00 2001 From: Akshita07 Date: Mon, 18 Jan 2021 21:51:15 +0100 Subject: [PATCH 51/81] update number to 3 (returns trace1 data) --- qcodes/instrument_drivers/Keysight/N9030B.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qcodes/instrument_drivers/Keysight/N9030B.py b/qcodes/instrument_drivers/Keysight/N9030B.py index 86155fe22584..1ceab1298ef5 100644 --- a/qcodes/instrument_drivers/Keysight/N9030B.py +++ b/qcodes/instrument_drivers/Keysight/N9030B.py @@ -350,7 +350,7 @@ def __init__(self, parent: "N9030B", name: str, *arg: Any, **kwargs: Any): name="trace", label="Trace", unit="dB", - number=1, + number=3, vals=Arrays(shape=(self.npts.get_latest,)), setpoints=(self.freq_axis,), parameter_class=Trace, From f3e3fbb2e880ec52569d4655717692cdcaae1176 Mon Sep 17 00:00:00 2001 From: Akshita07 Date: Tue, 19 Jan 2021 11:11:45 +0100 Subject: [PATCH 52/81] Add check for carrier missing for PN mode get_data --- qcodes/instrument_drivers/Keysight/N9030B.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/qcodes/instrument_drivers/Keysight/N9030B.py b/qcodes/instrument_drivers/Keysight/N9030B.py index 1ceab1298ef5..419ddf2df465 100644 --- a/qcodes/instrument_drivers/Keysight/N9030B.py +++ b/qcodes/instrument_drivers/Keysight/N9030B.py @@ -402,6 +402,16 @@ def _get_data(self, trace_num: int) -> ParamRawDataType: """ Gets data from the measurement. """ + raw_data = self.ask(f":READ:{self.root_instrument.measurement()}{1}?") + trace_res_details = np.array( + raw_data.rstrip().split(",") + ).astype("float64") + + if len(trace_res_details) != 7 or ( + len(trace_res_details) >= 1 and trace_res_details[0] < -50 + ): + raise RuntimeError("Carrier(s) Incorrect or Missing!") + try: data_str = self.ask(f":READ:{self.root_instrument.measurement()}" f"{trace_num}?") @@ -437,6 +447,8 @@ def autotune(self) -> None: Autotunes frequency """ self.write(":SENSe:FREQuency:CARRier:SEARch") + self.start_offset() + self.stop_offset() class N9030B(VisaInstrument): From 1b40c0b181ff33144eb7ebc90527b1c5c710b24a Mon Sep 17 00:00:00 2001 From: Akshita07 Date: Tue, 19 Jan 2021 11:37:02 +0100 Subject: [PATCH 53/81] log warning and return array of -1 for carrier missing for PN mode get_data --- qcodes/instrument_drivers/Keysight/N9030B.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/qcodes/instrument_drivers/Keysight/N9030B.py b/qcodes/instrument_drivers/Keysight/N9030B.py index 419ddf2df465..e45bf92abad7 100644 --- a/qcodes/instrument_drivers/Keysight/N9030B.py +++ b/qcodes/instrument_drivers/Keysight/N9030B.py @@ -410,7 +410,8 @@ def _get_data(self, trace_num: int) -> ParamRawDataType: if len(trace_res_details) != 7 or ( len(trace_res_details) >= 1 and trace_res_details[0] < -50 ): - raise RuntimeError("Carrier(s) Incorrect or Missing!") + self.log.warning("Carrier(s) Incorrect or Missing!") + return -1 * np.ones(self.npts) try: data_str = self.ask(f":READ:{self.root_instrument.measurement()}" From 31229ccbd928d78199340f5a655103961d80f6d9 Mon Sep 17 00:00:00 2001 From: Akshita07 Date: Tue, 19 Jan 2021 11:37:41 +0100 Subject: [PATCH 54/81] Add signal tracking param for PN mode --- qcodes/instrument_drivers/Keysight/N9030B.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/qcodes/instrument_drivers/Keysight/N9030B.py b/qcodes/instrument_drivers/Keysight/N9030B.py index e45bf92abad7..27cbb6d0d59b 100644 --- a/qcodes/instrument_drivers/Keysight/N9030B.py +++ b/qcodes/instrument_drivers/Keysight/N9030B.py @@ -334,6 +334,14 @@ def __init__(self, parent: "N9030B", name: str, *arg: Any, **kwargs: Any): docstring="stop frequency offset for the plot" ) + self.add_parameter( + name="signal_tracking_enabled", + get_cmd=":SENSe:FREQuency:CARRier:TRACk?", + set_cmd=":SENSe:FREQuency:CARRier:TRACk {}", + val_mapping=create_on_off_val_mapping(on_val="ON", off_val="OFF"), + docstring="Gets/Sets signal tracking" + ) + self.add_parameter( name="freq_axis", label="Frequency", From 50fc53ba0cfb530d4d640d72cb2eabf622c89cdf Mon Sep 17 00:00:00 2001 From: Akshita07 Date: Tue, 19 Jan 2021 11:43:34 +0100 Subject: [PATCH 55/81] add missing braces --- qcodes/instrument_drivers/Keysight/N9030B.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qcodes/instrument_drivers/Keysight/N9030B.py b/qcodes/instrument_drivers/Keysight/N9030B.py index 27cbb6d0d59b..95913d22a39a 100644 --- a/qcodes/instrument_drivers/Keysight/N9030B.py +++ b/qcodes/instrument_drivers/Keysight/N9030B.py @@ -419,7 +419,7 @@ def _get_data(self, trace_num: int) -> ParamRawDataType: len(trace_res_details) >= 1 and trace_res_details[0] < -50 ): self.log.warning("Carrier(s) Incorrect or Missing!") - return -1 * np.ones(self.npts) + return -1 * np.ones(self.npts()) try: data_str = self.ask(f":READ:{self.root_instrument.measurement()}" From 1a2335125af3bb9215e8bead906a80efaa47ea13 Mon Sep 17 00:00:00 2001 From: Akshita07 Date: Tue, 19 Jan 2021 11:54:32 +0100 Subject: [PATCH 56/81] update freq axis param --- qcodes/instrument_drivers/Keysight/N9030B.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/qcodes/instrument_drivers/Keysight/N9030B.py b/qcodes/instrument_drivers/Keysight/N9030B.py index 95913d22a39a..260e318963f1 100644 --- a/qcodes/instrument_drivers/Keysight/N9030B.py +++ b/qcodes/instrument_drivers/Keysight/N9030B.py @@ -13,12 +13,12 @@ class FrequencyAxis(Parameter): def __init__(self, start: float, stop: float, npts: int, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) - self._start: float = start - self._stop: float = stop - self._npts: int = npts + self._strt: float = start + self._stp: float = stop + self._npt: int = npts def get_raw(self) -> ParamRawDataType: - return np.linspace(self._start, self._stop, self._npts) + return np.linspace(self._strt, self._stp, self._npt) class Trace(ParameterWithSetpoints): From 0b143861a33a5ecb2d8910c9b117f154a070c8fd Mon Sep 17 00:00:00 2001 From: Akshita07 Date: Tue, 19 Jan 2021 12:33:12 +0100 Subject: [PATCH 57/81] add parenthesis to start, stop and npts calls --- qcodes/instrument_drivers/Keysight/N9030B.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/qcodes/instrument_drivers/Keysight/N9030B.py b/qcodes/instrument_drivers/Keysight/N9030B.py index 260e318963f1..3f79450e5da1 100644 --- a/qcodes/instrument_drivers/Keysight/N9030B.py +++ b/qcodes/instrument_drivers/Keysight/N9030B.py @@ -142,9 +142,9 @@ def __init__(self, parent: "N9030B", name: str, *arg: Any, **kwargs: Any): name="freq_axis", label="Frequency", unit="Hz", - start=self.start, - stop=self.stop, - npts=self.npts, + start=self.start(), + stop=self.stop(), + npts=self.npts(), vals=Arrays(shape=(self.npts.get_latest,)), parameter_class=FrequencyAxis, docstring="Sets frequency axis for the sweep." @@ -346,9 +346,9 @@ def __init__(self, parent: "N9030B", name: str, *arg: Any, **kwargs: Any): name="freq_axis", label="Frequency", unit="Hz", - start=self.start_offset, - stop=self.stop_offset, - npts=self.npts, + start=self.start_offset(), + stop=self.stop_offset(), + npts=self.npts(), vals=Arrays(shape=(self.npts.get_latest,)), parameter_class=FrequencyAxis, docstring="Sets frequency axis for the sweep." From e0bae37d1ee33b4ccd46f36f2e9cb1a32c448867 Mon Sep 17 00:00:00 2001 From: Akshita07 Date: Tue, 19 Jan 2021 12:53:38 +0100 Subject: [PATCH 58/81] Add signal tracking enabled to yaml --- qcodes/instrument/sims/Keysight_N9030B.yaml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/qcodes/instrument/sims/Keysight_N9030B.yaml b/qcodes/instrument/sims/Keysight_N9030B.yaml index eebb1306a880..270c3d170e9e 100644 --- a/qcodes/instrument/sims/Keysight_N9030B.yaml +++ b/qcodes/instrument/sims/Keysight_N9030B.yaml @@ -183,6 +183,17 @@ devices: specs: type: float + signal_tracking_enabled: + default: "ON" + getter: + q: ":SENSe:FREQuency:CARRier:TRACk?" + r: "{}" + setter: + q: ":SENSe:FREQuency:CARRier:TRACk {}" + specs: + type: str + valid: ["ON", "OFF"] + resources: GPIB::1::INSTR: device: KeysightN9030B From 2c87a81cf11d25617db4bc0345c0d474a84391b9 Mon Sep 17 00:00:00 2001 From: Trevor Morgan <63689909+trevormorgan@users.noreply.github.com> Date: Tue, 19 Jan 2021 22:41:58 -0800 Subject: [PATCH 59/81] edits to run unit tests --- qcodes/instrument/sims/Keysight_N9030B.yaml | 24 ++++++++++--------- qcodes/instrument_drivers/Keysight/N9030B.py | 25 ++++++++++---------- 2 files changed, 26 insertions(+), 23 deletions(-) diff --git a/qcodes/instrument/sims/Keysight_N9030B.yaml b/qcodes/instrument/sims/Keysight_N9030B.yaml index 270c3d170e9e..c7e1533b7e25 100644 --- a/qcodes/instrument/sims/Keysight_N9030B.yaml +++ b/qcodes/instrument/sims/Keysight_N9030B.yaml @@ -44,10 +44,13 @@ devices: q: ":INITiate:CONTinuous {}" channels: - sa: - dialogues: - - q: ":SENS:FREQuency:TUNE:IMMediate" - - q: ":READ:SANalyzer1?" + card: + ids: ['sa', 'pn'] + can_select: True + #sa: + # dialogues: + # - q: ":SENS:FREQuency:TUNE:IMMediate" + # - q: ":READ:SANalyzer1?" properties: @@ -144,14 +147,13 @@ devices: type: str valid: ["FFT", "SWE"] - pn: - dialogues: - - q: ":SENSe:FREQuency:CARRier:SEARch" - - q: ":READ:LPLot1?" + #pn: + # dialogues: + # - q: ":SENSe:FREQuency:CARRier:SEARch" + # - q: ":READ:LPLot1?" + # properties: - properties: - - npts: + lognpts: default: 601 getter: q: ":SENSe:LPLot:SWEep:POINts?" diff --git a/qcodes/instrument_drivers/Keysight/N9030B.py b/qcodes/instrument_drivers/Keysight/N9030B.py index 3f79450e5da1..9da9e4fdfa7b 100644 --- a/qcodes/instrument_drivers/Keysight/N9030B.py +++ b/qcodes/instrument_drivers/Keysight/N9030B.py @@ -38,6 +38,7 @@ class SpectrumAnalyzerMode(InstrumentChannel): """ Spectrum Analyzer Mode for Keysight N9030B instrument. """ + def __init__(self, parent: "N9030B", name: str, *arg: Any, **kwargs: Any): super().__init__(parent, name, *arg, **kwargs) @@ -308,7 +309,7 @@ def __init__(self, parent: "N9030B", name: str, *arg: Any, **kwargs: Any): self._max_freq = self._valid_max_freq[opt] self.add_parameter( - name="npts", + name="lognpts", get_cmd=":SENSe:LPLot:SWEep:POINts?", set_cmd=":SENSe:LPLot:SWEep:POINts {}", get_parser=int, @@ -348,8 +349,8 @@ def __init__(self, parent: "N9030B", name: str, *arg: Any, **kwargs: Any): unit="Hz", start=self.start_offset(), stop=self.stop_offset(), - npts=self.npts(), - vals=Arrays(shape=(self.npts.get_latest,)), + npts=self.lognpts(), + vals=Arrays(shape=(self.lognpts.get_latest,)), parameter_class=FrequencyAxis, docstring="Sets frequency axis for the sweep." ) @@ -359,7 +360,7 @@ def __init__(self, parent: "N9030B", name: str, *arg: Any, **kwargs: Any): label="Trace", unit="dB", number=3, - vals=Arrays(shape=(self.npts.get_latest,)), + vals=Arrays(shape=(self.lognpts.get_latest,)), setpoints=(self.freq_axis,), parameter_class=Trace, docstring="Gets trace data." @@ -524,20 +525,20 @@ def __init__(self, name: str, address: str, **kwargs: Any) -> None: ) if "SA" in self._available_modes(): - swept_sa = SpectrumAnalyzerMode(self, name="swept_sa") - sa_mode = ChannelList( - self, "sa", self.CHANNEL_CLASS_1, [swept_sa], snapshotable=True - ) + sa_mode = SpectrumAnalyzerMode(self, name="sa") + # sa_mode = ChannelList( + # self, "sa", self.CHANNEL_CLASS_1, [swept_sa], snapshotable=True + # ) self.add_submodule("sa", sa_mode) else: self.log.info("Spectrum Analyzer mode is not available on this " "instrument.") if "PNOISE" in self._available_modes(): - log_plot = PhaseNoiseMode(self, name="log_plot") - pnoise_mode = ChannelList( - self, "pn", self.CHANNEL_CLASS_2, [log_plot], snapshotable=True - ) + pnoise_mode = PhaseNoiseMode(self, name="pn") + # pnoise_mode = ChannelList( + # self, "pn", self.CHANNEL_CLASS_2, [log_plot], snapshotable=True + # ) self.add_submodule("pn", pnoise_mode) else: self.log.info("Phase Noise mode is not available on this " From d4117b5f90cac2de2a05249ea38935d4806f8e11 Mon Sep 17 00:00:00 2001 From: Akshita07 Date: Wed, 20 Jan 2021 14:45:00 +0100 Subject: [PATCH 60/81] Update tests file --- qcodes/tests/drivers/test_keysight_n9030b.py | 32 ++++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/qcodes/tests/drivers/test_keysight_n9030b.py b/qcodes/tests/drivers/test_keysight_n9030b.py index dd37dbd6aca0..a42725c747ff 100644 --- a/qcodes/tests/drivers/test_keysight_n9030b.py +++ b/qcodes/tests/drivers/test_keysight_n9030b.py @@ -15,13 +15,13 @@ def _make_driver(): driver.close() -@pytest.fixture(name="swept_sa") +@pytest.fixture(name="sa") def _activate_swept_sa_measurement(driver): - yield driver.sa.swept_sa + yield driver.sa -@pytest.fixture(name="log_plot") +@pytest.fixture(name="pn") def _activate_log_plot_measurement(driver): - yield driver.pn.log_plot + yield driver.pn def test_idn(driver): @@ -31,19 +31,19 @@ def test_idn(driver): 'vendor': 'Keysight Technologies'} == driver.IDN() -def test_swept_sa_setup(swept_sa): - assert isinstance(swept_sa, SpectrumAnalyzerMode) +def test_swept_sa_setup(sa): + assert isinstance(sa, SpectrumAnalyzerMode) - swept_sa.setup_swept_sa_sweep(1e3, 1e6, 501) - assert swept_sa.start() == 1e3 - assert swept_sa.stop() == 1e6 - assert swept_sa.npts() == 501 + sa.setup_swept_sa_sweep(1e3, 1e6, 501) + assert sa.start() == 1e3 + assert sa.stop() == 1e6 + assert sa.npts() == 501 -def test_log_plot_setup(log_plot): - assert isinstance(log_plot, PhaseNoiseMode) +def test_log_plot_setup(pn): + assert isinstance(pn, PhaseNoiseMode) - log_plot.setup_log_plot_sweep(100, 1000, 10001) - assert log_plot.start_offset() == 100 - assert log_plot.stop_offset() == 1000 - assert log_plot.npts() == 10001 + pn.setup_log_plot_sweep(100, 1000, 10001) + assert pn.start_offset() == 100 + assert pn.stop_offset() == 1000 + assert pn.lognpts() == 10001 From e82ad4cf6aecac5603fc5d50dcb20345c607c111 Mon Sep 17 00:00:00 2001 From: Akshita07 Date: Wed, 20 Jan 2021 14:45:57 +0100 Subject: [PATCH 61/81] Update yaml --- qcodes/instrument/sims/Keysight_N9030B.yaml | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/qcodes/instrument/sims/Keysight_N9030B.yaml b/qcodes/instrument/sims/Keysight_N9030B.yaml index c7e1533b7e25..09b08f6596a9 100644 --- a/qcodes/instrument/sims/Keysight_N9030B.yaml +++ b/qcodes/instrument/sims/Keysight_N9030B.yaml @@ -47,13 +47,10 @@ devices: card: ids: ['sa', 'pn'] can_select: True - #sa: - # dialogues: - # - q: ":SENS:FREQuency:TUNE:IMMediate" - # - q: ":READ:SANalyzer1?" properties: + #sa: start: default: 100 getter: @@ -65,7 +62,7 @@ devices: type: float stop: - default: 1000 + default: 10000 getter: q: ":SENSe:FREQuency:STOP?" r: "{}" @@ -146,13 +143,7 @@ devices: specs: type: str valid: ["FFT", "SWE"] - - #pn: - # dialogues: - # - q: ":SENSe:FREQuency:CARRier:SEARch" - # - q: ":READ:LPLot1?" - # properties: - + #pn lognpts: default: 601 getter: From bcc7157b4b7ba56a1d22185506c2e3489f1b1bc8 Mon Sep 17 00:00:00 2001 From: Akshita07 Date: Wed, 20 Jan 2021 14:47:55 +0100 Subject: [PATCH 62/81] Update freq axis param in driver --- qcodes/instrument_drivers/Keysight/N9030B.py | 42 ++++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/qcodes/instrument_drivers/Keysight/N9030B.py b/qcodes/instrument_drivers/Keysight/N9030B.py index 9da9e4fdfa7b..b529b1d66c98 100644 --- a/qcodes/instrument_drivers/Keysight/N9030B.py +++ b/qcodes/instrument_drivers/Keysight/N9030B.py @@ -1,8 +1,9 @@ import numpy as np from typing import Any, Tuple, Dict, Union -from qcodes import (VisaInstrument, InstrumentChannel, Parameter, - ParameterWithSetpoints, ChannelList) +from qcodes import ( + VisaInstrument, InstrumentChannel, Parameter, ParameterWithSetpoints +) from qcodes.instrument.parameter import ParamRawDataType from qcodes.utils.validators import Enum, Numbers, Arrays, Ints from qcodes.utils.helpers import create_on_off_val_mapping @@ -10,12 +11,17 @@ class FrequencyAxis(Parameter): - def __init__(self, start: float, stop: float, npts: int, *args: Any, - **kwargs: Any) -> None: + def __init__(self, + start: Parameter, + stop: Parameter, + npts: Parameter, + *args: Any, + **kwargs: Any + ) -> None: super().__init__(*args, **kwargs) - self._strt: float = start - self._stp: float = stop - self._npt: int = npts + self._strt: float = start() + self._stp: float = stop() + self._npt: int = npts() def get_raw(self) -> ParamRawDataType: return np.linspace(self._strt, self._stp, self._npt) @@ -143,9 +149,9 @@ def __init__(self, parent: "N9030B", name: str, *arg: Any, **kwargs: Any): name="freq_axis", label="Frequency", unit="Hz", - start=self.start(), - stop=self.stop(), - npts=self.npts(), + start=self.start, + stop=self.stop, + npts=self.npts, vals=Arrays(shape=(self.npts.get_latest,)), parameter_class=FrequencyAxis, docstring="Sets frequency axis for the sweep." @@ -347,9 +353,9 @@ def __init__(self, parent: "N9030B", name: str, *arg: Any, **kwargs: Any): name="freq_axis", label="Frequency", unit="Hz", - start=self.start_offset(), - stop=self.stop_offset(), - npts=self.lognpts(), + start=self.start_offset, + stop=self.stop_offset, + npts=self.lognpts, vals=Arrays(shape=(self.lognpts.get_latest,)), parameter_class=FrequencyAxis, docstring="Sets frequency axis for the sweep." @@ -420,7 +426,7 @@ def _get_data(self, trace_num: int) -> ParamRawDataType: len(trace_res_details) >= 1 and trace_res_details[0] < -50 ): self.log.warning("Carrier(s) Incorrect or Missing!") - return -1 * np.ones(self.npts()) + return -1 * np.ones(self.lognpts()) try: data_str = self.ask(f":READ:{self.root_instrument.measurement()}" @@ -450,7 +456,7 @@ def setup_log_plot_sweep(self, self.start_offset(start_offset) self.stop_offset(stop_offset) - self.npts(npts) + self.lognpts(npts) def autotune(self) -> None: """ @@ -526,9 +532,6 @@ def __init__(self, name: str, address: str, **kwargs: Any) -> None: if "SA" in self._available_modes(): sa_mode = SpectrumAnalyzerMode(self, name="sa") - # sa_mode = ChannelList( - # self, "sa", self.CHANNEL_CLASS_1, [swept_sa], snapshotable=True - # ) self.add_submodule("sa", sa_mode) else: self.log.info("Spectrum Analyzer mode is not available on this " @@ -536,9 +539,6 @@ def __init__(self, name: str, address: str, **kwargs: Any) -> None: if "PNOISE" in self._available_modes(): pnoise_mode = PhaseNoiseMode(self, name="pn") - # pnoise_mode = ChannelList( - # self, "pn", self.CHANNEL_CLASS_2, [log_plot], snapshotable=True - # ) self.add_submodule("pn", pnoise_mode) else: self.log.info("Phase Noise mode is not available on this " From 1cc7ad8de2c78aa09424e9be6e6ef5a845dfab46 Mon Sep 17 00:00:00 2001 From: Akshita07 Date: Wed, 20 Jan 2021 16:49:28 +0100 Subject: [PATCH 63/81] Update tests --- qcodes/tests/drivers/test_keysight_n9030b.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/qcodes/tests/drivers/test_keysight_n9030b.py b/qcodes/tests/drivers/test_keysight_n9030b.py index a42725c747ff..ab2e840d1b20 100644 --- a/qcodes/tests/drivers/test_keysight_n9030b.py +++ b/qcodes/tests/drivers/test_keysight_n9030b.py @@ -34,16 +34,14 @@ def test_idn(driver): def test_swept_sa_setup(sa): assert isinstance(sa, SpectrumAnalyzerMode) - sa.setup_swept_sa_sweep(1e3, 1e6, 501) - assert sa.start() == 1e3 - assert sa.stop() == 1e6 - assert sa.npts() == 501 + sa.setup_swept_sa_sweep(100, 10e3, 501) + assert sa.root_instrument.mode() == "SA" + assert sa.root_instrument.measurement() == "SAN" def test_log_plot_setup(pn): assert isinstance(pn, PhaseNoiseMode) - pn.setup_log_plot_sweep(100, 1000, 10001) - assert pn.start_offset() == 100 - assert pn.stop_offset() == 1000 - assert pn.lognpts() == 10001 + pn.setup_log_plot_sweep(100, 1e6, 10001) + assert pn.root_instrument.mode() == "PNOISE" + assert pn.root_instrument.measurement() == "LPL" From 3965b0774eca00589e69c918038801d1eb2adbe3 Mon Sep 17 00:00:00 2001 From: Akshita07 Date: Wed, 20 Jan 2021 16:59:47 +0100 Subject: [PATCH 64/81] mypy fixes --- qcodes/instrument_drivers/Keysight/N9030B.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/qcodes/instrument_drivers/Keysight/N9030B.py b/qcodes/instrument_drivers/Keysight/N9030B.py index b529b1d66c98..0d1ce1f987ff 100644 --- a/qcodes/instrument_drivers/Keysight/N9030B.py +++ b/qcodes/instrument_drivers/Keysight/N9030B.py @@ -19,9 +19,9 @@ def __init__(self, **kwargs: Any ) -> None: super().__init__(*args, **kwargs) - self._strt: float = start() - self._stp: float = stop() - self._npt: int = npts() + self._strt: float = start.get() + self._stp: float = stop.get() + self._npt: int = npts.get() def get_raw(self) -> ParamRawDataType: return np.linspace(self._strt, self._stp, self._npt) From 7d9e9ca012b4e5ccadb01d870dd15b8c1263cd51 Mon Sep 17 00:00:00 2001 From: Trevor Morgan <63689909+trevormorgan@users.noreply.github.com> Date: Wed, 20 Jan 2021 20:42:30 -0800 Subject: [PATCH 65/81] fix to freq axis parameter --- qcodes/instrument_drivers/Keysight/N9030B.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/qcodes/instrument_drivers/Keysight/N9030B.py b/qcodes/instrument_drivers/Keysight/N9030B.py index 0d1ce1f987ff..9081fff939ec 100644 --- a/qcodes/instrument_drivers/Keysight/N9030B.py +++ b/qcodes/instrument_drivers/Keysight/N9030B.py @@ -19,12 +19,12 @@ def __init__(self, **kwargs: Any ) -> None: super().__init__(*args, **kwargs) - self._strt: float = start.get() - self._stp: float = stop.get() - self._npt: int = npts.get() + self._strt: Parameter = start + self._stp: Parameter = stop + self._npt: Parameter = npts def get_raw(self) -> ParamRawDataType: - return np.linspace(self._strt, self._stp, self._npt) + return np.linspace(self._strt(), self._stp(), self._npt()) class Trace(ParameterWithSetpoints): From e54c8c90a91cb6b1b3afcdcc91055c6f4e8ff6df Mon Sep 17 00:00:00 2001 From: Trevor Morgan <63689909+trevormorgan@users.noreply.github.com> Date: Wed, 20 Jan 2021 22:44:59 -0800 Subject: [PATCH 66/81] working pyvisa-sim yaml --- qcodes/instrument/sims/Keysight_N9030B.yaml | 13 ++++++++----- qcodes/instrument_drivers/Keysight/N9030B.py | 13 +++++++------ qcodes/tests/drivers/test_keysight_n9030b.py | 6 +++++- 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/qcodes/instrument/sims/Keysight_N9030B.yaml b/qcodes/instrument/sims/Keysight_N9030B.yaml index 09b08f6596a9..e2586abb990c 100644 --- a/qcodes/instrument/sims/Keysight_N9030B.yaml +++ b/qcodes/instrument/sims/Keysight_N9030B.yaml @@ -44,13 +44,12 @@ devices: q: ":INITiate:CONTinuous {}" channels: - card: - ids: ['sa', 'pn'] + sa: + ids: ['sa'] can_select: True properties: - #sa: start: default: 100 getter: @@ -143,8 +142,12 @@ devices: specs: type: str valid: ["FFT", "SWE"] - #pn - lognpts: + pn: + ids: ['pn'] + can_select: True + + properties: + npts: default: 601 getter: q: ":SENSe:LPLot:SWEep:POINts?" diff --git a/qcodes/instrument_drivers/Keysight/N9030B.py b/qcodes/instrument_drivers/Keysight/N9030B.py index 9081fff939ec..18297378a30e 100644 --- a/qcodes/instrument_drivers/Keysight/N9030B.py +++ b/qcodes/instrument_drivers/Keysight/N9030B.py @@ -1,3 +1,4 @@ +from qcodes.instrument.channel import ChannelList import numpy as np from typing import Any, Tuple, Dict, Union @@ -315,7 +316,7 @@ def __init__(self, parent: "N9030B", name: str, *arg: Any, **kwargs: Any): self._max_freq = self._valid_max_freq[opt] self.add_parameter( - name="lognpts", + name="npts", get_cmd=":SENSe:LPLot:SWEep:POINts?", set_cmd=":SENSe:LPLot:SWEep:POINts {}", get_parser=int, @@ -355,8 +356,8 @@ def __init__(self, parent: "N9030B", name: str, *arg: Any, **kwargs: Any): unit="Hz", start=self.start_offset, stop=self.stop_offset, - npts=self.lognpts, - vals=Arrays(shape=(self.lognpts.get_latest,)), + npts=self.npts, + vals=Arrays(shape=(self.npts.get_latest,)), parameter_class=FrequencyAxis, docstring="Sets frequency axis for the sweep." ) @@ -366,7 +367,7 @@ def __init__(self, parent: "N9030B", name: str, *arg: Any, **kwargs: Any): label="Trace", unit="dB", number=3, - vals=Arrays(shape=(self.lognpts.get_latest,)), + vals=Arrays(shape=(self.npts.get_latest,)), setpoints=(self.freq_axis,), parameter_class=Trace, docstring="Gets trace data." @@ -426,7 +427,7 @@ def _get_data(self, trace_num: int) -> ParamRawDataType: len(trace_res_details) >= 1 and trace_res_details[0] < -50 ): self.log.warning("Carrier(s) Incorrect or Missing!") - return -1 * np.ones(self.lognpts()) + return -1 * np.ones(self.npts()) try: data_str = self.ask(f":READ:{self.root_instrument.measurement()}" @@ -456,7 +457,7 @@ def setup_log_plot_sweep(self, self.start_offset(start_offset) self.stop_offset(stop_offset) - self.lognpts(npts) + self.npts(npts) def autotune(self) -> None: """ diff --git a/qcodes/tests/drivers/test_keysight_n9030b.py b/qcodes/tests/drivers/test_keysight_n9030b.py index ab2e840d1b20..dfb7330b24e6 100644 --- a/qcodes/tests/drivers/test_keysight_n9030b.py +++ b/qcodes/tests/drivers/test_keysight_n9030b.py @@ -19,6 +19,7 @@ def _make_driver(): def _activate_swept_sa_measurement(driver): yield driver.sa + @pytest.fixture(name="pn") def _activate_log_plot_measurement(driver): yield driver.pn @@ -34,10 +35,13 @@ def test_idn(driver): def test_swept_sa_setup(sa): assert isinstance(sa, SpectrumAnalyzerMode) - sa.setup_swept_sa_sweep(100, 10e3, 501) + sa.setup_swept_sa_sweep(123, 11e3, 501) assert sa.root_instrument.mode() == "SA" assert sa.root_instrument.measurement() == "SAN" + assert sa.start() == 123 + assert sa.stop() == 11e3 + def test_log_plot_setup(pn): assert isinstance(pn, PhaseNoiseMode) From 175b3084e4adea8f6901c5c6e0aa0c7e6229229f Mon Sep 17 00:00:00 2001 From: Akshita07 Date: Thu, 21 Jan 2021 08:52:33 +0100 Subject: [PATCH 67/81] Remove unused import and raises errors in except --- qcodes/instrument_drivers/Keysight/N9030B.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/qcodes/instrument_drivers/Keysight/N9030B.py b/qcodes/instrument_drivers/Keysight/N9030B.py index 18297378a30e..1c03a2d4faa8 100644 --- a/qcodes/instrument_drivers/Keysight/N9030B.py +++ b/qcodes/instrument_drivers/Keysight/N9030B.py @@ -1,4 +1,3 @@ -from qcodes.instrument.channel import ChannelList import numpy as np from typing import Any, Tuple, Dict, Union @@ -257,7 +256,7 @@ def _get_data(self, trace_num: int) -> ParamRawDataType: f"{trace_num}?") data = np.array(data_str.rstrip().split(",")).astype("float64") except TimeoutError: - pass + raise RuntimeError("Couldn't receive data. Command timed out.") trace_data = data[1::2] return trace_data @@ -434,7 +433,7 @@ def _get_data(self, trace_num: int) -> ParamRawDataType: f"{trace_num}?") data = np.array(data_str.rstrip().split(",")).astype("float64") except TimeoutError: - pass + raise RuntimeError("Couldn't receive any data. Command timed out.") trace_data = data[1::2] return trace_data From b84f9edc8c6d04e77be374ea2a4c5e86bde6552f Mon Sep 17 00:00:00 2001 From: Akshita07 Date: Thu, 21 Jan 2021 08:52:57 +0100 Subject: [PATCH 68/81] Update tests --- qcodes/tests/drivers/test_keysight_n9030b.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/qcodes/tests/drivers/test_keysight_n9030b.py b/qcodes/tests/drivers/test_keysight_n9030b.py index dfb7330b24e6..db5604bcf1f1 100644 --- a/qcodes/tests/drivers/test_keysight_n9030b.py +++ b/qcodes/tests/drivers/test_keysight_n9030b.py @@ -41,11 +41,17 @@ def test_swept_sa_setup(sa): assert sa.start() == 123 assert sa.stop() == 11e3 + assert sa.npts() == 501 def test_log_plot_setup(pn): assert isinstance(pn, PhaseNoiseMode) - pn.setup_log_plot_sweep(100, 1e6, 10001) + pn.setup_log_plot_sweep(1000, 1e7, 10001) assert pn.root_instrument.mode() == "PNOISE" assert pn.root_instrument.measurement() == "LPL" + + assert pn.start_offset() == 1000 + assert pn.stop_offset() == 1e7 + assert pn.npts() == 10001 + From 80097869a973c6250949b20979f8d3491c0ab37b Mon Sep 17 00:00:00 2001 From: Akshita07 Date: Thu, 21 Jan 2021 09:45:02 +0100 Subject: [PATCH 69/81] Remove extra line from the end of test file --- qcodes/tests/drivers/test_keysight_n9030b.py | 1 - 1 file changed, 1 deletion(-) diff --git a/qcodes/tests/drivers/test_keysight_n9030b.py b/qcodes/tests/drivers/test_keysight_n9030b.py index db5604bcf1f1..c54d24d37800 100644 --- a/qcodes/tests/drivers/test_keysight_n9030b.py +++ b/qcodes/tests/drivers/test_keysight_n9030b.py @@ -54,4 +54,3 @@ def test_log_plot_setup(pn): assert pn.start_offset() == 1000 assert pn.stop_offset() == 1e7 assert pn.npts() == 10001 - From 2e9db810ebfc948e1dad61db8685d2e4975dbee1 Mon Sep 17 00:00:00 2001 From: Akshita07 Date: Thu, 21 Jan 2021 09:48:48 +0100 Subject: [PATCH 70/81] raise error from e --- qcodes/instrument_drivers/Keysight/N9030B.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/qcodes/instrument_drivers/Keysight/N9030B.py b/qcodes/instrument_drivers/Keysight/N9030B.py index 1c03a2d4faa8..83c64d153838 100644 --- a/qcodes/instrument_drivers/Keysight/N9030B.py +++ b/qcodes/instrument_drivers/Keysight/N9030B.py @@ -255,8 +255,9 @@ def _get_data(self, trace_num: int) -> ParamRawDataType: f"{self.root_instrument.measurement()}" f"{trace_num}?") data = np.array(data_str.rstrip().split(",")).astype("float64") - except TimeoutError: - raise RuntimeError("Couldn't receive data. Command timed out.") + except TimeoutError as e: + raise RuntimeError("Couldn't receive any data. Command timed " + "out.") from e trace_data = data[1::2] return trace_data @@ -432,8 +433,9 @@ def _get_data(self, trace_num: int) -> ParamRawDataType: data_str = self.ask(f":READ:{self.root_instrument.measurement()}" f"{trace_num}?") data = np.array(data_str.rstrip().split(",")).astype("float64") - except TimeoutError: - raise RuntimeError("Couldn't receive any data. Command timed out.") + except TimeoutError as e: + raise RuntimeError("Couldn't receive any data. Command timed " + "out.") from e trace_data = data[1::2] return trace_data From d0762a81ae36c46166523b2376c89dc1f02ec035 Mon Sep 17 00:00:00 2001 From: Akshita07 Date: Thu, 21 Jan 2021 10:13:12 +0100 Subject: [PATCH 71/81] Update notebook --- .../Qcodes example with Keysight N9030B.ipynb | 29 ++++++++++++------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/docs/examples/driver_examples/Qcodes example with Keysight N9030B.ipynb b/docs/examples/driver_examples/Qcodes example with Keysight N9030B.ipynb index 8a1869c036df..08bea8411480 100644 --- a/docs/examples/driver_examples/Qcodes example with Keysight N9030B.ipynb +++ b/docs/examples/driver_examples/Qcodes example with Keysight N9030B.ipynb @@ -65,7 +65,7 @@ "metadata": {}, "outputs": [], "source": [ - "swept_sa = driver.sa.swept_sa" + "sa = driver.sa" ] }, { @@ -74,7 +74,7 @@ "metadata": {}, "outputs": [], "source": [ - "swept_sa.setup_swept_sa_sweep(start=200, stop= 10e3, npts=20001)" + "sa.setup_swept_sa_sweep(start=200, stop= 10e3, npts=20001)" ] }, { @@ -99,7 +99,7 @@ "source": [ "tutorial_db_path = os.path.join(os.getcwd(), 'tutorial.db')\n", "initialise_or_create_database_at(tutorial_db_path)\n", - "load_or_create_experiment(experiment_name='tutorial_exp', sample_name=\"no sample\")" + "load_or_create_experiment(experiment_name='tutorial_exp1', sample_name=\"no sample1\")" ] }, { @@ -109,7 +109,7 @@ "outputs": [], "source": [ "meas1 = Measurement()\n", - "meas1.register_parameter(swept_sa.trace)" + "meas1.register_parameter(sa.trace)" ] }, { @@ -119,7 +119,7 @@ "outputs": [], "source": [ "with meas1.run() as datasaver:\n", - " datasaver.add_result((swept_sa.trace, swept_sa.trace.get()))\n", + " datasaver.add_result((sa.trace, sa.trace.get()))\n", "\n", "dataset = datasaver.dataset" ] @@ -153,7 +153,7 @@ "metadata": {}, "outputs": [], "source": [ - "log_plot = driver.pn.log_plot" + "pn = driver.pn" ] }, { @@ -162,7 +162,7 @@ "metadata": {}, "outputs": [], "source": [ - "log_plot.setup_log_plot_sweep(start_offset=10, stop_offset=200, npts=1001)" + "pn.setup_log_plot_sweep(start_offset=10, stop_offset=200, npts=1001)" ] }, { @@ -172,6 +172,15 @@ "### With QCoDeS Measurement" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "load_or_create_experiment(experiment_name='tutorial_exp2', sample_name=\"no sample2\")" + ] + }, { "cell_type": "code", "execution_count": null, @@ -179,7 +188,7 @@ "outputs": [], "source": [ "meas2 = Measurement()\n", - "meas2.register_parameter(log_plot.trace)" + "meas2.register_parameter(pn.trace)" ] }, { @@ -188,8 +197,8 @@ "metadata": {}, "outputs": [], "source": [ - "with meas1.run() as datasaver:\n", - " datasaver.add_result((log_plot.trace, log_plot.trace.get()))\n", + "with meas2.run() as datasaver:\n", + " datasaver.add_result((pn.trace, pn.trace.get()))\n", "\n", "run_id = datasaver.run_id" ] From 932986079b8fe9629dc9b954a3db577d07d7ab1f Mon Sep 17 00:00:00 2001 From: Akshita07 Date: Thu, 21 Jan 2021 10:30:19 +0100 Subject: [PATCH 72/81] Correct usage of enumerate --- qcodes/instrument_drivers/Keysight/N9030B.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/qcodes/instrument_drivers/Keysight/N9030B.py b/qcodes/instrument_drivers/Keysight/N9030B.py index 83c64d153838..7bee0dee87c6 100644 --- a/qcodes/instrument_drivers/Keysight/N9030B.py +++ b/qcodes/instrument_drivers/Keysight/N9030B.py @@ -554,11 +554,11 @@ def _available_modes(self) -> Tuple[str, ...]: available_modes = self.ask(":INSTrument:CATalog?") av_modes = available_modes[1:-1].split(',') modes: Tuple[str, ...] = () - for i, _ in enumerate(av_modes): + for i, mode in enumerate(av_modes): if i == 0: - modes = modes + (av_modes[i].split(' ')[0],) + modes = modes + (mode.split(' ')[0], ) else: - modes = modes + (av_modes[i].split(' ')[1],) + modes = modes + (mode.split(' ')[1], ) return modes def _available_meas(self) -> Tuple[str, ...]: @@ -567,13 +567,13 @@ def _available_meas(self) -> Tuple[str, ...]: """ available_meas = self.ask(":CONFigure:CATalog?") av_meas = available_meas[1:-1].split(',') - meas: Tuple[str, ...] = () - for i, _ in enumerate(av_meas): + measurements: Tuple[str, ...] = () + for i, meas in enumerate(av_meas): if i == 0: - meas = meas + (av_meas[i],) + measurements = measurements + (meas, ) else: - meas = meas + (av_meas[i][1:],) - return meas + measurements = measurements + (meas[1:], ) + return measurements def _enable_cont_meas(self, val: str) -> None: """ From 5d2a451550c2a4c29176bfac1f3d9c693f91e35a Mon Sep 17 00:00:00 2001 From: Akshita07 Date: Thu, 21 Jan 2021 10:32:40 +0100 Subject: [PATCH 73/81] Update variable names --- qcodes/instrument_drivers/Keysight/N9030B.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/qcodes/instrument_drivers/Keysight/N9030B.py b/qcodes/instrument_drivers/Keysight/N9030B.py index 7bee0dee87c6..c6dcb4fdc3d7 100644 --- a/qcodes/instrument_drivers/Keysight/N9030B.py +++ b/qcodes/instrument_drivers/Keysight/N9030B.py @@ -19,12 +19,12 @@ def __init__(self, **kwargs: Any ) -> None: super().__init__(*args, **kwargs) - self._strt: Parameter = start - self._stp: Parameter = stop - self._npt: Parameter = npts + self._start: Parameter = start + self._stop: Parameter = stop + self._npts: Parameter = npts def get_raw(self) -> ParamRawDataType: - return np.linspace(self._strt(), self._stp(), self._npt()) + return np.linspace(self._start(), self._stop(), self._npts()) class Trace(ParameterWithSetpoints): From 50b7ee10a3a240556005806599a81a7d73b4317c Mon Sep 17 00:00:00 2001 From: Akshita07 Date: Thu, 21 Jan 2021 10:58:03 +0100 Subject: [PATCH 74/81] Add units --- qcodes/instrument_drivers/Keysight/N9030B.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/qcodes/instrument_drivers/Keysight/N9030B.py b/qcodes/instrument_drivers/Keysight/N9030B.py index c6dcb4fdc3d7..a8291cdd0427 100644 --- a/qcodes/instrument_drivers/Keysight/N9030B.py +++ b/qcodes/instrument_drivers/Keysight/N9030B.py @@ -62,6 +62,7 @@ def __init__(self, parent: "N9030B", name: str, *arg: Any, **kwargs: Any): self.add_parameter( name="start", + unit="Hz", get_cmd=":SENSe:FREQuency:STARt?", set_cmd=self._set_start, get_parser=float, @@ -71,6 +72,7 @@ def __init__(self, parent: "N9030B", name: str, *arg: Any, **kwargs: Any): self.add_parameter( name="stop", + unit="Hz", get_cmd=":SENSe:FREQuency:STOP?", set_cmd=self._set_stop, get_parser=float, @@ -80,6 +82,7 @@ def __init__(self, parent: "N9030B", name: str, *arg: Any, **kwargs: Any): self.add_parameter( name="center", + unit="Hz", get_cmd=":SENSe:FREQuency:CENTer?", set_cmd=self._set_center, get_parser=float, @@ -89,6 +92,7 @@ def __init__(self, parent: "N9030B", name: str, *arg: Any, **kwargs: Any): self.add_parameter( name="span", + unit="Hz", get_cmd=":SENSe:FREQuency:SPAN?", set_cmd=self._set_span, get_parser=float, @@ -326,6 +330,7 @@ def __init__(self, parent: "N9030B", name: str, *arg: Any, **kwargs: Any): self.add_parameter( name="start_offset", + unit="Hz", get_cmd=":SENSe:LPLot:FREQuency:OFFSet:STARt?", set_cmd=self._set_start_offset, get_parser=float, @@ -335,6 +340,7 @@ def __init__(self, parent: "N9030B", name: str, *arg: Any, **kwargs: Any): self.add_parameter( name="stop_offset", + unit="Hz", get_cmd=":SENSe:LPLot:FREQuency:OFFSet:STOP?", set_cmd=self._set_stop_offset, get_parser=float, From 7dce6d4b083ed25da8d5dd2036e206ec28f50fe6 Mon Sep 17 00:00:00 2001 From: Akshita07 Date: Thu, 21 Jan 2021 10:59:30 +0100 Subject: [PATCH 75/81] Update doc strings --- qcodes/instrument_drivers/Keysight/N9030B.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/qcodes/instrument_drivers/Keysight/N9030B.py b/qcodes/instrument_drivers/Keysight/N9030B.py index a8291cdd0427..686d987d676c 100644 --- a/qcodes/instrument_drivers/Keysight/N9030B.py +++ b/qcodes/instrument_drivers/Keysight/N9030B.py @@ -158,7 +158,8 @@ def __init__(self, parent: "N9030B", name: str, *arg: Any, **kwargs: Any): npts=self.npts, vals=Arrays(shape=(self.npts.get_latest,)), parameter_class=FrequencyAxis, - docstring="Sets frequency axis for the sweep." + docstring="Creates frequency axis for the sweep from start, " + "stop and npts values." ) self.add_parameter( @@ -294,9 +295,11 @@ def setup_swept_sa_sweep(self, def autotune(self) -> None: """ - Autotunes frequency + Autotune quickly get to the most likely signal of interest, and + position it optimally on the display. """ self.write(":SENS:FREQuency:TUNE:IMMediate") + self.center() class PhaseNoiseMode(InstrumentChannel): @@ -353,7 +356,10 @@ def __init__(self, parent: "N9030B", name: str, *arg: Any, **kwargs: Any): get_cmd=":SENSe:FREQuency:CARRier:TRACk?", set_cmd=":SENSe:FREQuency:CARRier:TRACk {}", val_mapping=create_on_off_val_mapping(on_val="ON", off_val="OFF"), - docstring="Gets/Sets signal tracking" + docstring="Gets/Sets signal tracking. When signal tracking is " + "enabled carrier signal is repeatedly realigned. Signal " + "Tracking assumes the new acquisition occurs repeatedly " + "without pause." ) self.add_parameter( @@ -365,7 +371,8 @@ def __init__(self, parent: "N9030B", name: str, *arg: Any, **kwargs: Any): npts=self.npts, vals=Arrays(shape=(self.npts.get_latest,)), parameter_class=FrequencyAxis, - docstring="Sets frequency axis for the sweep." + docstring="Creates frequency axis for the sweep from " + "start_offset, stop_offset and npts values." ) self.add_parameter( @@ -468,7 +475,8 @@ def setup_log_plot_sweep(self, def autotune(self) -> None: """ - Autotunes frequency + On autotune, the measurement automatically searches for and tunes to + the strongest signal in the full span of the analyzer. """ self.write(":SENSe:FREQuency:CARRier:SEARch") self.start_offset() From d73b15f47d0dc1f72339e53905af96fed9d67f8c Mon Sep 17 00:00:00 2001 From: Akshita07 Date: Thu, 21 Jan 2021 11:00:10 +0100 Subject: [PATCH 76/81] Raise correct errors --- qcodes/instrument_drivers/Keysight/N9030B.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/qcodes/instrument_drivers/Keysight/N9030B.py b/qcodes/instrument_drivers/Keysight/N9030B.py index 686d987d676c..430eb19e3e1e 100644 --- a/qcodes/instrument_drivers/Keysight/N9030B.py +++ b/qcodes/instrument_drivers/Keysight/N9030B.py @@ -260,9 +260,8 @@ def _get_data(self, trace_num: int) -> ParamRawDataType: f"{self.root_instrument.measurement()}" f"{trace_num}?") data = np.array(data_str.rstrip().split(",")).astype("float64") - except TimeoutError as e: - raise RuntimeError("Couldn't receive any data. Command timed " - "out.") from e + except TimeoutError: + raise TimeoutError("Couldn't receive any data. Command timed out.") trace_data = data[1::2] return trace_data @@ -446,9 +445,8 @@ def _get_data(self, trace_num: int) -> ParamRawDataType: data_str = self.ask(f":READ:{self.root_instrument.measurement()}" f"{trace_num}?") data = np.array(data_str.rstrip().split(",")).astype("float64") - except TimeoutError as e: - raise RuntimeError("Couldn't receive any data. Command timed " - "out.") from e + except TimeoutError: + raise TimeoutError("Couldn't receive any data. Command timed out.") trace_data = data[1::2] return trace_data From d41c0060c19f58bffd3190ca3ed82836b2c94129 Mon Sep 17 00:00:00 2001 From: Akshita07 Date: Thu, 21 Jan 2021 11:00:40 +0100 Subject: [PATCH 77/81] Remove unused statements --- qcodes/instrument_drivers/Keysight/N9030B.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/qcodes/instrument_drivers/Keysight/N9030B.py b/qcodes/instrument_drivers/Keysight/N9030B.py index 430eb19e3e1e..fde710d848d5 100644 --- a/qcodes/instrument_drivers/Keysight/N9030B.py +++ b/qcodes/instrument_drivers/Keysight/N9030B.py @@ -494,9 +494,6 @@ class N9030B(VisaInstrument): address """ - CHANNEL_CLASS_1 = SpectrumAnalyzerMode - CHANNEL_CLASS_2 = PhaseNoiseMode - def __init__(self, name: str, address: str, **kwargs: Any) -> None: super().__init__(name, address, terminator='\n', **kwargs) From c4d146f1e32ea6b8352616f2289be7b7ff47262d Mon Sep 17 00:00:00 2001 From: Akshita07 Date: Thu, 21 Jan 2021 11:12:35 +0100 Subject: [PATCH 78/81] raise error from e --- qcodes/instrument_drivers/Keysight/N9030B.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/qcodes/instrument_drivers/Keysight/N9030B.py b/qcodes/instrument_drivers/Keysight/N9030B.py index fde710d848d5..b241369c73b8 100644 --- a/qcodes/instrument_drivers/Keysight/N9030B.py +++ b/qcodes/instrument_drivers/Keysight/N9030B.py @@ -260,8 +260,9 @@ def _get_data(self, trace_num: int) -> ParamRawDataType: f"{self.root_instrument.measurement()}" f"{trace_num}?") data = np.array(data_str.rstrip().split(",")).astype("float64") - except TimeoutError: - raise TimeoutError("Couldn't receive any data. Command timed out.") + except TimeoutError as e: + raise TimeoutError("Couldn't receive any data. Command timed " + "out.") from e trace_data = data[1::2] return trace_data @@ -445,8 +446,9 @@ def _get_data(self, trace_num: int) -> ParamRawDataType: data_str = self.ask(f":READ:{self.root_instrument.measurement()}" f"{trace_num}?") data = np.array(data_str.rstrip().split(",")).astype("float64") - except TimeoutError: - raise TimeoutError("Couldn't receive any data. Command timed out.") + except TimeoutError as e: + raise TimeoutError("Couldn't receive any data. Command timed " + "out.") from e trace_data = data[1::2] return trace_data From 8617d4fb737c8dc370b10ae0551ec1c589e79046 Mon Sep 17 00:00:00 2001 From: Akshita07 Date: Fri, 22 Jan 2021 14:31:25 +0100 Subject: [PATCH 79/81] remove new exp for 2nd run and plottr section --- .../Qcodes example with Keysight N9030B.ipynb | 32 +------------------ 1 file changed, 1 insertion(+), 31 deletions(-) diff --git a/docs/examples/driver_examples/Qcodes example with Keysight N9030B.ipynb b/docs/examples/driver_examples/Qcodes example with Keysight N9030B.ipynb index 08bea8411480..d722c65918e1 100644 --- a/docs/examples/driver_examples/Qcodes example with Keysight N9030B.ipynb +++ b/docs/examples/driver_examples/Qcodes example with Keysight N9030B.ipynb @@ -99,7 +99,7 @@ "source": [ "tutorial_db_path = os.path.join(os.getcwd(), 'tutorial.db')\n", "initialise_or_create_database_at(tutorial_db_path)\n", - "load_or_create_experiment(experiment_name='tutorial_exp1', sample_name=\"no sample1\")" + "load_or_create_experiment(experiment_name='tutorial_exp', sample_name=\"no sample\")" ] }, { @@ -172,15 +172,6 @@ "### With QCoDeS Measurement" ] }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "load_or_create_experiment(experiment_name='tutorial_exp2', sample_name=\"no sample2\")" - ] - }, { "cell_type": "code", "execution_count": null, @@ -218,27 +209,6 @@ "source": [ "_ = plot_by_id(run_id)" ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Live plotting with Plottr" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "For live plotting, you can use Plottr package with Qcodes. For more information about this, head to [How to use plottr with QCoDeS for live plotting](../plotting/How-to-use-Plottr-with-QCoDeS-for-live-plotting.ipynb)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] } ], "metadata": { From 458819916734dfb00a2be6041d4afebd1bdadb90 Mon Sep 17 00:00:00 2001 From: Akshita07 Date: Fri, 22 Jan 2021 14:35:39 +0100 Subject: [PATCH 80/81] remove initial values for auto sweep time/type enabled --- qcodes/instrument_drivers/Keysight/N9030B.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/qcodes/instrument_drivers/Keysight/N9030B.py b/qcodes/instrument_drivers/Keysight/N9030B.py index b241369c73b8..a4cc303203c7 100644 --- a/qcodes/instrument_drivers/Keysight/N9030B.py +++ b/qcodes/instrument_drivers/Keysight/N9030B.py @@ -121,7 +121,6 @@ def __init__(self, parent: "N9030B", name: str, *arg: Any, **kwargs: Any): self.add_parameter( name="auto_sweep_time_enabled", - initial_value=False, get_cmd=":SENSe:SWEep:TIME:AUTO?", set_cmd=self._enable_auto_sweep_time, val_mapping=create_on_off_val_mapping(on_val="ON", off_val="OFF"), @@ -130,7 +129,6 @@ def __init__(self, parent: "N9030B", name: str, *arg: Any, **kwargs: Any): self.add_parameter( name="auto_sweep_type_enabled", - initial_value=False, get_cmd=":SENSe:SWEep:TYPE:AUTO?", set_cmd=self._enable_auto_sweep_type, val_mapping=create_on_off_val_mapping(on_val="ON", off_val="OFF"), From 4d1d7f162f20ff9b6076263b00ad2915c43c13b3 Mon Sep 17 00:00:00 2001 From: Akshita07 Date: Tue, 26 Jan 2021 12:38:25 +0100 Subject: [PATCH 81/81] Upload executed example notebook --- .../Qcodes example with Keysight N9030B.ipynb | 176 +++++++++++++++--- 1 file changed, 152 insertions(+), 24 deletions(-) diff --git a/docs/examples/driver_examples/Qcodes example with Keysight N9030B.ipynb b/docs/examples/driver_examples/Qcodes example with Keysight N9030B.ipynb index d722c65918e1..c135b3ba91f1 100644 --- a/docs/examples/driver_examples/Qcodes example with Keysight N9030B.ipynb +++ b/docs/examples/driver_examples/Qcodes example with Keysight N9030B.ipynb @@ -18,7 +18,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "metadata": { "ExecuteTime": { "end_time": "2021-01-14T15:11:25.180448Z", @@ -28,6 +28,7 @@ "outputs": [], "source": [ "import numpy as np\n", + "import os\n", "from qcodes.dataset import initialise_or_create_database_at, load_or_create_experiment, Measurement\n", "from qcodes.dataset.plotting import plot_dataset, plot_by_id\n", "\n", @@ -36,18 +37,40 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Connected to: Keysight Technologies N9030B (serial:MY56320646, firmware:A.24.57) in 0.35s\n" + ] + } + ], "source": [ - "driver = N9030B(\"n9030b\",\"GPIB_Address\")" + "driver = N9030B(\"n9030b\",\"driver_address\")" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "{'vendor': 'Keysight Technologies',\n", + " 'model': 'N9030B',\n", + " 'serial': 'MY56320646',\n", + " 'firmware': 'A.24.57'}" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "driver.IDN()" ] @@ -61,7 +84,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ @@ -70,7 +93,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 5, "metadata": {}, "outputs": [], "source": [ @@ -93,9 +116,36 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 6, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Upgrading database; v0 -> v1: : 0it [00:00, ?it/s]\n", + "Upgrading database; v1 -> v2: 100%|█████████████████████████████████████████████████████| 1/1 [00:00<00:00, 334.71it/s]\n", + "Upgrading database; v2 -> v3: : 0it [00:00, ?it/s]\n", + "Upgrading database; v3 -> v4: : 0it [00:00, ?it/s]\n", + "Upgrading database; v4 -> v5: 100%|█████████████████████████████████████████████████████| 1/1 [00:00<00:00, 167.42it/s]\n", + "Upgrading database; v5 -> v6: : 0it [00:00, ?it/s]\n", + "Upgrading database; v6 -> v7: 100%|██████████████████████████████████████████████████████| 1/1 [00:00<00:00, 91.64it/s]\n", + "Upgrading database; v7 -> v8: 100%|█████████████████████████████████████████████████████| 1/1 [00:00<00:00, 202.59it/s]\n", + "Upgrading database; v8 -> v9: 100%|█████████████████████████████████████████████████████| 1/1 [00:00<00:00, 202.59it/s]\n" + ] + }, + { + "data": { + "text/plain": [ + "tutorial_exp#no sample#1@C:\\Users\\asic\\Documents\\tutorial.db\n", + "------------------------------------------------------------" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "tutorial_db_path = os.path.join(os.getcwd(), 'tutorial.db')\n", "initialise_or_create_database_at(tutorial_db_path)\n", @@ -104,9 +154,20 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 7, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "meas1 = Measurement()\n", "meas1.register_parameter(sa.trace)" @@ -114,9 +175,17 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 8, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Starting experimental run with id: 1. \n" + ] + } + ], "source": [ "with meas1.run() as datasaver:\n", " datasaver.add_result((sa.trace, sa.trace.get()))\n", @@ -133,9 +202,22 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 9, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], "source": [ "_ = plot_dataset(dataset)" ] @@ -149,7 +231,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 10, "metadata": {}, "outputs": [], "source": [ @@ -158,7 +240,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 11, "metadata": {}, "outputs": [], "source": [ @@ -174,9 +256,20 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 12, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "meas2 = Measurement()\n", "meas2.register_parameter(pn.trace)" @@ -184,9 +277,24 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 13, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Starting experimental run with id: 2. \n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "[n9030b_pn(PhaseNoiseMode)] Carrier(s) Incorrect or Missing!\n" + ] + } + ], "source": [ "with meas2.run() as datasaver:\n", " datasaver.add_result((pn.trace, pn.trace.get()))\n", @@ -203,12 +311,32 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 14, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], "source": [ "_ = plot_by_id(run_id)" ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": {