Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions docs/changes/newsfragments/8602.improved_driver
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Refactored the ``CopperMountainM5xxx`` base driver to work more generally
with all S2VNAs (now called ``CopperMountainS2VnaBase``) and added a driver for V0602.
The V0602 VISA simulator supports reset, averaging count, and electrical-distance
units for driver testing.
141 changes: 141 additions & 0 deletions src/qcodes/instrument/sims/CopperMountain_V0602.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
spec: "1.0"

resources:
TCPIP0::localhost::hislip0::INSTR:
device: V0602

devices:
V0602:
eom:
TCPIP INSTR:
q: "\n"
r: "\n"
error: ERROR
dialogues:
- q: "*IDN?"
r: "CMT, V0602, 23047001, 25.3.1/2"
- q: "*RST"
properties:
output:
default: 0
getter:
q: "OUTP:STAT?"
r: "{}"
setter:
q: "OUTP:STAT {}"
specs:
type: int
valid: [0, 1]
power:
default: -20
getter:
q: "SOUR:POW?"
r: "{}"
setter:
q: "SOUR:POW {}"
specs:
min: -50
max: 5
type: float
if_bandwidth:
default: 100000
getter:
q: "SENS1:BWID?"
r: "{}"
setter:
q: "SENS1:BWID {}"
averages_enabled:
default: 0
getter:
q: "SENS1:AVER:STAT?"
r: "{}"
setter:
q: "SENS1:AVER:STAT {}"
specs:
type: int
valid: [0, 1]
averages_count:
default: 1
getter:
q: "SENS1:AVER:COUN?"
r: "{}"
setter:
q: "SENS1:AVER:COUN {}"
specs:
type: int
min: 1
averages_trigger_enabled:
default: 0
getter:
q: "TRIG:SEQ:AVER?"
r: "{}"
setter:
q: "TRIG:SEQ:AVER {}"
specs:
type: int
valid: [0, 1]
electrical_delay:
default: 0
getter:
q: "CALC1:CORR:EDEL:TIME?"
r: "{}"
setter:
q: "CALC1:CORR:EDEL:TIME {}"
electrical_distance:
default: 20
getter:
q: "CALC1:CORR:EDEL:DIST?"
r: "{}"
setter:
q: "CALC1:CORR:EDEL:DIST {}"
electrical_distance_unit:
default: MET
getter:
q: "CALC1:CORR:EDEL:DIST:UNIT?"
r: "{}"
setter:
q: "CALC1:CORR:EDEL:DIST:UNIT {}"
clock_source:
default: "INT"
getter:
q: "SENSe1:ROSCillator:SOURce?"
r: "{}"
setter:
q: "SENSe1:ROSCillator:SOURce {}"
start:
default: 300000
getter:
q: "SENS1:FREQ:STAR?"
r: "{}"
setter:
q: "SENS1:FREQ:STAR {}"
specs:
type: float
stop:
default: 6500000000
getter:
q: "SENS1:FREQ:STOP?"
r: "{}"
setter:
q: "SENS1:FREQ:STOP {}"
specs:
type: float
center:
getter:
q: "SENS1:FREQ:CENT?"
r: "{}"
setter:
q: "SENS1:FREQ:CENT {}"
span:
getter:
q: "SENS1:FREQ:SPAN?"
r: "{}"
setter:
q: "SENS1:FREQ:SPAN {}"
number_of_points:
default: 201
getter:
q: "SENS1:SWE:POIN?"
r: "{}"
setter:
q: "SENS1:SWE:POIN {}"
15 changes: 12 additions & 3 deletions src/qcodes/instrument_drivers/CopperMountain/_M5065.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
from typing import TYPE_CHECKING

from ._M5xxx import CopperMountainM5xxx
from ._s2vna_base import CopperMountainS2VnaBase

if TYPE_CHECKING:
from typing import Unpack

from qcodes.instrument import VisaInstrumentKWArgs


class CopperMountainM5065(CopperMountainM5xxx):
class CopperMountainM5065(CopperMountainS2VnaBase):
"""This is the QCoDeS driver for the M5065 VNA from Copper Mountain Technologies"""

def __init__(
Expand All @@ -17,4 +17,13 @@ def __init__(
address: str,
**kwargs: "Unpack[VisaInstrumentKWArgs]",
):
super().__init__(name, address, min_freq=300e3, max_freq=6.5e9, **kwargs)
super().__init__(
name,
address,
min_freq=300e3,
max_freq=6.5e9,
min_power=-55.0,
max_power=5.0,
max_points_per_sweep=200_001,
**kwargs,
)
15 changes: 12 additions & 3 deletions src/qcodes/instrument_drivers/CopperMountain/_M5180.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
from typing import TYPE_CHECKING

from ._M5xxx import CopperMountainM5xxx
from ._s2vna_base import CopperMountainS2VnaBase

if TYPE_CHECKING:
from typing import Unpack

from qcodes.instrument import VisaInstrumentKWArgs


class CopperMountainM5180(CopperMountainM5xxx):
class CopperMountainM5180(CopperMountainS2VnaBase):
"""This is the QCoDeS driver for the M5180 VNA from Copper Mountain Technologies"""

def __init__(
Expand All @@ -17,4 +17,13 @@ def __init__(
address: str,
**kwargs: "Unpack[VisaInstrumentKWArgs]",
):
super().__init__(name, address, min_freq=300e3, max_freq=18e9, **kwargs)
super().__init__(
name,
address,
min_freq=300e3,
max_freq=18e9,
min_power=-40,
max_power=10,
max_points_per_sweep=200_001,
**kwargs,
)
10 changes: 8 additions & 2 deletions src/qcodes/instrument_drivers/CopperMountain/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
from ._M5xxx import CopperMountainM5xxx
from ._M5065 import CopperMountainM5065
from ._M5180 import CopperMountainM5180
from ._s2vna_base import CopperMountainS2VnaBase
from ._v0602 import CopperMountainV0602

__all__ = ["CopperMountainM5xxx", "CopperMountainM5065", "CopperMountainM5180"]
__all__ = [
"CopperMountainM5065",
"CopperMountainM5180",
"CopperMountainS2VnaBase",
"CopperMountainV0602",
]
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@
from typing import Unpack


class CopperMountainM5xxx(VisaInstrument):
class CopperMountainS2VnaBase(VisaInstrument):
"""
Base class for QCoDeS drivers for Copper Mountain M-series VNAs.
Base class for QCoDeS drivers for Copper Mountain S2VNAs.

Not to be instantiated directly. Use model specific subclass.
https://coppermountaintech.com/help-s2/index.html
Expand All @@ -37,26 +37,37 @@ def __init__(
address: str,
min_freq: float,
max_freq: float,
min_power: float,
max_power: float,
max_points_per_sweep: int,
**kwargs: "Unpack[VisaInstrumentKWArgs]",
):
"""
QCoDeS driver for Copper Mountain M-series VNA (M5xxx).
QCoDeS Base driver for Copper Mountain S2VNAs. This should not be
instantiated directly. Please see https://coppermountaintech.com/help-s2/index.html
for list of compatible instruments.

This driver supports only a single channel.

Args:
name: Identifier for the instrument instance.
address: VISA address of the instrument.
min_freq: Minimum frequency supported by the instrument (in Hz).
max_freq: Maximum frequency supported by the instrument (in Hz).
min_power: Minimum output power (in dBm).
max_power: Maximum output power (in dBm).
max_points_per_sweep: Maximum number of measurement points per sweep.
**kwargs: Additional keyword arguments for VisaInstrument.

"""

super().__init__(name=name, address=address, **kwargs)
self.min_freq = min_freq
self.max_freq = max_freq
self.min_points = 2
self.max_points = 200001
self._min_freq = min_freq
self._max_freq = max_freq
self._min_power = min_power
self._max_power = max_power
self._min_points_per_sweep = 2
self._max_points_per_sweep = max_points_per_sweep

self.output: Parameter = self.add_parameter(
name="output",
Expand All @@ -76,7 +87,7 @@ def __init__(
set_cmd="SOUR:POW {}",
unit="dBm",
docstring="Sets or reads out the power level for the frequency sweep type in dBm.",
vals=Numbers(min_value=-55, max_value=5),
vals=Numbers(min_value=self._min_power, max_value=self._max_power),
)
"""Sets or reads out the power level for the frequency sweep type in dBm."""

Expand Down Expand Up @@ -203,7 +214,7 @@ def __init__(
unit="Hz",
docstring="Sets or reads out the stimulus start value "
"of the sweep range for linear or logarithmic sweep type.",
vals=Numbers(min_value=self.min_freq, max_value=self.max_freq - 1),
vals=Numbers(min_value=self._min_freq, max_value=self._max_freq - 1),
)
"""Sets or reads out the stimulus start value of the sweep
range for linear or logarithmic sweep type."""
Expand All @@ -217,7 +228,7 @@ def __init__(
unit="Hz",
docstring="Sets or reads out the stimulus stop value of the "
"sweep range for linear or logarithmic sweep type.",
vals=Numbers(min_value=self.min_freq + 1, max_value=self.max_freq),
vals=Numbers(min_value=self._min_freq + 1, max_value=self._max_freq),
)
"""Sets or reads out the stimulus stop value of the sweep
range for linear or logarithmic sweep type."""
Expand All @@ -231,7 +242,7 @@ def __init__(
unit="Hz",
docstring="Sets or reads out the stimulus center value of "
"the sweep range for linear or logarithmic sweep type.",
vals=Numbers(min_value=self.min_freq + 1, max_value=self.max_freq - 1),
vals=Numbers(min_value=self._min_freq + 1, max_value=self._max_freq - 1),
)
"""Sets or reads out the stimulus center value of the sweep range for
linear or logarithmic sweep type."""
Expand All @@ -245,7 +256,7 @@ def __init__(
unit="Hz",
docstring="Sets or reads out the stimulus span value of the "
"sweep range for linear or logarithmic sweep type.",
vals=Numbers(min_value=1, max_value=self.max_freq - 1),
vals=Numbers(min_value=1, max_value=self._max_freq - 1),
)
"""Sets or reads out the stimulus span value of the sweep range
for linear or logarithmic sweep type."""
Expand All @@ -258,7 +269,10 @@ def __init__(
get_cmd="SENS1:SWE:POIN?",
set_cmd=self._set_number_of_points,
docstring="Sets or reads out the number of measurement points.",
vals=Ints(min_value=self.min_points, max_value=self.max_points),
vals=Ints(
min_value=self._min_points_per_sweep,
max_value=self._max_points_per_sweep,
),
)
"""Sets or reads out the number of measurement points."""

Expand Down Expand Up @@ -625,7 +639,7 @@ def _db(data: "NDArray") -> "NDArray":


class FrequencySweepMagPhase(
MultiParameter[tuple[NDArray, NDArray], CopperMountainM5xxx]
MultiParameter[tuple[NDArray, NDArray], CopperMountainS2VnaBase]
):
"""
Sweep that returns magnitude and phase.
Expand All @@ -637,7 +651,7 @@ def __init__(
start: float,
stop: float,
number_of_points: int,
instrument: CopperMountainM5xxx,
instrument: CopperMountainS2VnaBase,
expected_measurement_duration: float = 600,
**kwargs: Any,
) -> None:
Expand Down Expand Up @@ -735,7 +749,7 @@ def get_raw(self) -> tuple[NDArray, NDArray]:


class PointMagPhase(
MultiParameter[tuple[np.floating, np.floating], CopperMountainM5xxx]
MultiParameter[tuple[np.floating, np.floating], CopperMountainS2VnaBase]
):
"""
Returns the average Sxx of a frequency sweep.
Expand All @@ -746,7 +760,7 @@ class PointMagPhase(
def __init__(
self,
name: str,
instrument: CopperMountainM5xxx,
instrument: CopperMountainS2VnaBase,
expected_measurement_duration: float = 600,
**kwargs: Any,
) -> None:
Expand Down Expand Up @@ -834,7 +848,7 @@ def get_raw(self) -> tuple[ParamRawDataType, ParamRawDataType]:
return 20 * np.log10(abs(sxx_mean)), (np.angle(sxx_mean))


class PointIQ(MultiParameter[tuple[np.floating, np.floating], CopperMountainM5xxx]):
class PointIQ(MultiParameter[tuple[np.floating, np.floating], CopperMountainS2VnaBase]):
"""
Returns the average Sxx of a frequency sweep, in terms of I and Q.
Work around for a CW mode where only one point is read.
Expand All @@ -844,7 +858,7 @@ class PointIQ(MultiParameter[tuple[np.floating, np.floating], CopperMountainM5xx
def __init__(
self,
name: str,
instrument: CopperMountainM5xxx,
instrument: CopperMountainS2VnaBase,
expected_measurement_duration: float = 600,
**kwargs: Any,
) -> None:
Expand Down
Loading
Loading