Source code for qcodes_contrib_drivers.drivers.Siglent.sds
from .sdx import SiglentSDx
from enum import Enum
from attr import dataclass
import re
import numpy as np
import numpy.typing as npt
from typing import Callable, Tuple
[docs]
class TriggerMode(Enum):
AUTO = "AUTO"
NORMAL = "NORM"
SINGLE = "SINGLE"
STOP = "STOP"
def __str__(self):
return self.value
[docs]
@dataclass
class WaveformSetup:
"""
Waveform download setup
spacing
SP: 0 or 1 = every point, n = every n-th
num_points:
NP: 0 = default, all
start_idx
FP: 0 is default
"""
spacing: int = 1
num_points: int = 0
start_idx: int = 0
# Not a proper QCoDeS instrument
# TODO: Add channels, add parameters, add parameter axes
[docs]
class Siglent_SDS_120NxE(SiglentSDx):
"""
Siglent SDS 1202/1204xE
"""
[docs]
def get_time_base(
self,
channel: int = 1,
*,
_RE=re.compile(r"^TDIV[ ]*([0-9eE+\-.,]+)[ ]*[sS]$"),
) -> int:
response = self.ask("TDIV?")
groups = _RE.match(response)
assert groups is not None
value = float(groups[1].replace(",", "."))
return int(value)
[docs]
def get_num_samples(
self,
channel: int = 1,
*,
_RE=re.compile(r"^SANU[ ]*([0-9eE+\-.,]+)[ ]*([kK]?)[ ]*pts$"),
) -> int:
response = self.ask(f"SANU? C{channel:d}")
groups = _RE.match(response)
assert groups is not None
value = float(groups[1].replace(",", "."))
if groups[2]:
value *= 1000
return int(value)
[docs]
def get_sample_rate(
self,
*,
_RE=re.compile(r"^SARA[ ]*([0-9eE+\-.,]+)[ ]*([kKMG]?)[ ]*[sS]a[ ]*/[ ]*s$"),
) -> int:
response = self.ask("SARA?")
groups = _RE.match(response)
assert groups is not None
value = float(groups[1].replace(",", "."))
multiplier = {
"k": 1e3,
"K": 1e3,
"M": 1e6,
"G": 1e9,
}
value *= multiplier.get(groups[2], 1.0)
return int(value)
[docs]
def get_vdiv(
self,
channel: int = 1,
*,
_RE=re.compile(r"^C[0-9]+:VDIV[ ]*([0-9eE+\-.,]+)[ ]*V$"),
) -> float:
response = self.ask(f"C{channel:d}:VDIV?")
groups = _RE.match(response)
assert groups is not None
value = float(groups[1].replace(",", "."))
return value
[docs]
def get_ofst(
self,
channel: int = 1,
*,
_RE=re.compile(r"^C[0-9]+:OFST[ ]*([0-9eE+\-.,]+)[ ]*V$"),
) -> float:
response = self.ask(f"C{channel:d}:OFST?")
groups = _RE.match(response)
assert groups is not None
value = float(groups[1].replace(",", "."))
return value
[docs]
def get_raw_analog_waveform_data(self, channel: int = 1) -> npt.NDArray:
return self.visa_handle.query_binary_values(
f"C{channel:d}:WF? DAT2",
"b",
header_fmt="ieee",
container=np.array, # type: ignore
)
[docs]
def get_math_vdiv(
self,
*,
_RE=re.compile(r"^MTVD[ ]*([0-9eE+\-.,]+)[ ]*V$"),
) -> float:
response = self.ask("MTVD?")
groups = _RE.match(response)
assert groups is not None
value = float(groups[1].replace(",", "."))
return value
[docs]
def get_raw_math_waveform_data(self) -> npt.NDArray:
self.write("MATH:WF? DAT2")
len_prefix = len("MATH:WF ALL,")
response_header = self.visa_handle.read_bytes(count=len_prefix) # noqa: F841
return self.visa_handle.read_binary_values(
"b",
header_fmt="ieee",
container=np.array, # type: ignore
)
[docs]
def get_raw_digital_waveform_data(self, channel: int = 0) -> npt.NDArray:
"Returns digital data. Each bit will be"
self.write(f"D{channel:d}:WF? DAT2")
len_prefix = len(f"D{channel:d}:WF ALL,")
response_part1 = self.visa_handle.read_bytes(count=len_prefix) # noqa: F841
return self.visa_handle.read_binary_values(
"B",
header_fmt="ieee",
container=np.array, # type: ignore
)
[docs]
def get_channel_waveform_data(self, channel: int) -> npt.NDArray:
Vdiv = self.get_vdiv(channel)
Vofs = self.get_ofst(channel)
return Vdiv * self.get_raw_analog_waveform_data(channel) / 25 + Vofs
def _get_waveform_axis(
self,
sample_rate: float,
wfsu: WaveformSetup,
get_num_samples: Callable[[], int],
) -> npt.NDArray:
num_points = wfsu.num_points if wfsu.num_points != 0 else get_num_samples()
spacing = wfsu.spacing if wfsu.spacing != 0 else 1
start_idx = wfsu.start_idx
return (np.arange(num_points) * spacing + start_idx) / sample_rate
[docs]
def get_channel_waveform(self, channel: int) -> Tuple[npt.NDArray, npt.NDArray]:
sample_rate = self.get_sample_rate()
wfsu = self.get_waveform_setup()
data = self.get_channel_waveform_data(channel)
axis = self._get_waveform_axis(
sample_rate, wfsu, get_num_samples=lambda: len(data)
)
return (axis, data)
[docs]
def get_math_waveform(self) -> npt.NDArray:
MtDiv = self.get_math_vdiv()
return MtDiv * self.get_raw_math_waveform_data() / 25
# don't use this. fft data can't be downloaded
[docs]
def get_trig_mode(self) -> TriggerMode:
response = self.ask("TRIG_MODE?")
tail = len("TRMD ")
return TriggerMode(response[tail:])
[docs]
def get_waveform_setup(
self,
*,
_RE=re.compile(
"WFSU [ ]*SP[ ]*,[ ]*([0-9]+),[ ]*NP[ ]*,[ ]*([0-9]+),[ ]*FP[ ]*,[ ]*([0-9]+)", # noqa: E501
re.IGNORECASE,
),
) -> WaveformSetup: # (SP, NP, FP)
response = self.ask("WFSU?")
groups = _RE.match(response)
assert groups is not None
return WaveformSetup(
spacing=int(groups[1]), num_points=int(groups[2]), start_idx=int(groups[3])
)
[docs]
def set_waveform_setup(self, wfsu: WaveformSetup):
self.write(f"WFSU SP,{wfsu.spacing},NP,{wfsu.num_points},FP,{wfsu.start_idx}")