zhinst / zhinst-toolkit Goto Github PK
View Code? Open in Web Editor NEWGeneric high-level interfaces for Zurich Instruments devices
License: MIT License
Generic high-level interfaces for Zurich Instruments devices
License: MIT License
Tests are run currently only on Python3.7, it would be great to have workflows for each supported Python version.
For now, it would mean: 3.7, 3.8, 3.9 and 3.10.
Coverage could be set for specific Python version, for example, 3.9.
Transactions are the way to go for changing multiple values in a row.
In zhinst-toolkit the are implemented in the NodeTree class. Since every device creates its own NodeTree object a transaction is limited to the nodes of the device.
in ziPython it is possible to change nodes of different devices within a single transaction, as long as they are connected through the same DataServer.
It would be nice to support the same feature in toolkit. A possible implementation could be to add a set_transaction Generator on the session that starts a transaction on each device/NodeTree and combines all the node-value pairs into one single set
command at the end.
device1 = session.connect_device("dev2345")
device2 = session.connect_device("dev5432")
with session.set_transaction():
device1.a.b(1)
device2.c.d("test")
would result in
daq.set(("/dev2345/a/b", 1),("/dev5432/c/d","test"))
In a transactional set, all the node setting are batched and sent together to the device only at the end of the block. That provide a significant speedup, but it's dangerous for functions that rely on the read value of a node.
For example, the .run()
function start a scope, enable the scope and check that it successfully enabled. But in a transactional set, the enable set is merely batched, so the check fails unless the scope was already enabled.
Introduce a decorator to mark such function as unsuitable for a transactional set. That will prevent dangerous situation and enforce correct usage
The device type of awg node in UHFQA is 0 rather than the name string
qa = session.connect_device("dev2571")
print(qa.awgs[0]._device_type)
UHFQA
It seems this bug is due to wrong argument in uhfli.py, the index is feed to the device type when initializing awg node.
With the 0.2.2 version of zhinst-toolkit, the CommandTable was introduced. This causes problems in our setup:
The first problem can easily be demonstrated:
Python 3.9.7 (tags/v3.9.7:1016ef3, Aug 30 2021, 20:19:38) [MSC v.1929 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license()" for more information.
>>> import urllib.request
>>> with urllib.request.urlopen("https://docs.zhinst.com/hdawg/commandtable/v2/schema") as f:
ct_schema = f.read().decode()
Traceback (most recent call last):
File "<pyshell#9>", line 1, in <module>
with urllib.request.urlopen("https://docs.zhinst.com/hdawg/commandtable/v2/schema") as f:
File "C:\Users\ruuddejong\AppData\Local\Programs\Python\Python39\lib\urllib\request.py", line 214, in urlopen
return opener.open(url, data, timeout)
File "C:\Users\ruuddejong\AppData\Local\Programs\Python\Python39\lib\urllib\request.py", line 523, in open
response = meth(req, response)
File "C:\Users\ruuddejong\AppData\Local\Programs\Python\Python39\lib\urllib\request.py", line 632, in http_response
response = self.parent.error(
File "C:\Users\ruuddejong\AppData\Local\Programs\Python\Python39\lib\urllib\request.py", line 561, in error
return self._call_chain(*args)
File "C:\Users\ruuddejong\AppData\Local\Programs\Python\Python39\lib\urllib\request.py", line 494, in _call_chain
result = func(*args)
File "C:\Users\ruuddejong\AppData\Local\Programs\Python\Python39\lib\urllib\request.py", line 641, in http_error_default
raise HTTPError(req.full_url, code, msg, hdrs, fp)
urllib.error.HTTPError: HTTP Error 403: Forbidden
Searching for similar situations, it would appear that the server requires a known user agent in the HTTP request, which is not provided in the current call to urlopen in zhinst.toolkit.control.drivers.base.ct.py.
To address the second problem, I feel it would probably be better to include the schema file in the zhinst-toolkit package directly. That would eliminate the need for a working internet connection / working zhinst server.
The `wait_for_state_change`-function of a `Node` is useful for waiting until a node is set to a certain value. But what is the desired semantic for a wildcard-node? Maybe that one associated leaf-node reaches the value, or maybe all. By the sequential nature of the following implementation, several problems arise. Since a error is raised if `node.wait_for_state_change` on the first leaf-node is not successful, all other leaf-nodes associated with the wildcard-node will never be taken into account. Even if we change that, the sequential nature would cause the function to wait a `timeout`-period for each leaf-node the wildcard-node resolves to. Thus potentially a high multiple of what the user intended to wait.
```python
# within wait_for_state_change
if self.node_info.contains_wildcards:
start_time = time.time()
nodes_raw = self._resolve_wildcards()
if not nodes_raw:
raise KeyError(self.node_info.path)
for node_raw in nodes_raw:
node = self._root.raw_path_to_node(node_raw)
node.wait_for_state_change(
value,
invert=invert,
timeout=max(0, timeout - (time.time() - start_time)),
sleep_time=sleep_time,
)
`wait_for_state_change` does not work with partial nodes either, as `curr_value = self._get(deep=True)[1]` excepts `self._get` to return a tuple. But for partial nodes, it returns a dictionary with multiple values.
Maybe `wait_for_state_change` should be declared to accept non-wildcard leaf-nodes only, because other applications are rare and may cause confusion.
How to reproduce
----------------
Call `wait_for_state_change` on any legal wildcard-node, which resolves to multiple leaf-nodes.
Environment info
----------------
- Python version: 3.11
- zhinst-toolkit version:
- LabOne version:
- zhinst version:
Hi there,
The sync keyword appears to be missing in the _set method of zhinst.toolkit.control.drivers.base.daq.DAQModule. I'm using zhinst-qcodes 0.2.0, zhinst-toolkit 0.2.0, and qcodes 0.28.0.
If I want to set a parameter, I get the following error message:
from zhinst.qcodes import UHFLI
uhfli = UHFLI("uhfli_1", "dev2110")
uhfli.daq.grid_cols(100)
Traceback (most recent call last):
File "C:\Users\haroldm\AppData\Local\Temp/ipykernel_3684/3767187837.py", line 1, in <module>
uhfli.daq.grid_cols(100)
File "c:\users\haroldm\repositories\low-level-software\env\lib\site-packages\qcodes\instrument\parameter.py", line 453, in __call__
self.set(*args, **kwargs)
File "c:\users\haroldm\repositories\low-level-software\env\lib\site-packages\qcodes\instrument\parameter.py", line 706, in set_wrapper
raise e
File "c:\users\haroldm\repositories\low-level-software\env\lib\site-packages\qcodes\instrument\parameter.py", line 690, in set_wrapper
set_function(raw_val_step, **kwargs)
File "c:\users\haroldm\repositories\low-level-software\env\lib\site-packages\qcodes\utils\command.py", line 180, in __call__
return self.exec_function(*args)
File "c:\users\haroldm\repositories\low-level-software\env\lib\site-packages\zhinst\toolkit\control\node_tree.py", line 350, in __call__
return self._setter(value, sync)
File "c:\users\haroldm\repositories\low-level-software\env\lib\site-packages\zhinst\toolkit\control\node_tree.py", line 204, in _setter
self._device._set(self._path, value, sync=sync)
TypeError: ("_set() got an unexpected keyword argument 'sync'", 'setting uhfli_1_daq_grid_cols to 100')
If I take a look at the method, the sync keyword is indeed not there:
def _set(self, *args):
if self._module is None:
_logger.error(
"This DAQ is not connected to a dataAcquisitionModule!",
_logger.ExceptionTypes.ToolkitConnectionError,
)
return self._module.set(*args, device=self._parent.serial)
I was hoping you could take a look at this.
Sincerely,
Harold
Hi,
I realize this isn't the best place to raise this, but I couldn't find another way of reaching out :')
I'm one of the maintainers of quantify, and our package depends on a few zhinst packages. We have a GitLab pipeline that creates a sphinx documentation, and one of the plugins, intersphinx
, creates a mapping between zhinst
related objects and a hyperlink to the corresponding API page of your package. As of yesterday, we noticed that when we build the documentation with a GitLab runner we run into a 403 error, meaning our connection is refused. The error message we get is:
intersphinx inventory 'https://docs.zhinst.com/zhinst-toolkit/en/latest/objects.inv' not fetchable due to <class 'requests.exceptions.HTTPError'>: 403 Client Error: Forbidden for url: https://docs.zhinst.com/zhinst-toolkit/en/latest/objects.inv
WARNING: failed to reach any of the inventories with the following issues:
intersphinx inventory 'https://docs.zhinst.com/zhinst-qcodes/en/v0.1/objects.inv' not fetchable due to <class 'requests.exceptions.HTTPError'>: 403 Client Error: Forbidden for url: https://docs.zhinst.com/zhinst-qcodes/en/v0.1/objects.inv
building documentation locally works fine, it's only the GitLab runners that are being blocked. Could it be that CloudFlare or a firewall is blocking us?
Play a time-varying Rabi sequence on outputs 1 and 2, then play a constant readout pulse on outputs 3 and 4.
The pulse sequence is something like this:
Using HDAWG4 in grouping mode = 1 (i.e. 1x4), the outputs allowed should be 1 or 2 or 3, or 4.
However, when using output number 3 or 4, on assignWaveIndex(3,readout_pulse)
the following error is displayed on compilation:
Compilation failed: Compiler Error (line: 47): can't play channel 3, valid values for channel are from 1 to 2
Can anyone please guide me as to what can be done?
I would also like the first marker on output 3 and the second marker on output 4 to be turned on with the readout pulse, however, trying to include an 11*markers=np.ones_like(readout)
does not seem to work.
control_pulses
is a list of pulses to be indexed and uploaded.
awg_program = Sequence()
awg_program.waveforms = Waveforms()
awg_program.waveforms.assign_waveform(0,wave1 = Wave(pul, name= f"readout_pulse"))
for i,pul in enumerate(control_pulses):
awg_program.waveforms.assign_waveform(i+1,Wave(pul, name= f"rabi_{i+1}"))
awg_program.code = """
while (true) {
"""
for i in range(len(control_pulses)):
awg_program.code += f"playWave(1, rabi_{i+1});\n"
awg_program.code += f"waitWave();\nplayWave(3,readout);\n"
awg_program.code += f"wait({wait_n});\n"
awg_program.code += '}'
elf_file, info = device.awgs[0].compile_sequencer_program(awg_program)
info
at this point the error comes up. If that weren't the case, I'd use the following:
with device.set_transaction():
device.awgs[0].elf.data(elf_file, deep=True)
device.awgs[0].write_to_waveform_memory(awg_program.waveforms)
# device.awgs[1].write_to_waveform_memory(awg_program.waveforms)
device.awgs[0].single(False) #Repeat sequence
device.awgs[0].enable(True)
Please let me know if waveforms also need to be uploaded to awg[1].
Using HDAWG4
Hi there,
If you get a value from a parameter that is an enumeration, you get an error when you try to pickle it, e.g.:
Traceback (most recent call last):
File "C:\Users\XXX\AppData\Local\Temp\ipykernel_14224\1814195601.py", line 1, in <cell line: 1>
loads(dumps(uhfli.demods.demods0.adcselect()))
PicklingError: Can't pickle <enum '/dev2338/demods/0/adcselect'>: attribute lookup /dev2338/demods/0/adcselect on zhinst.toolkit.nodetree.node failed
Getting the value works fine. No mention of attribute lookup failed there.
We send the instrument's snapshot to a parameter monitor in another process and pickle the values.
from zhinst.qcodes.driver.devices import UHFLI
from zhinst.toolkit.driver.devices import UHFLI as tkUHFLI
from zhinst.toolkit import Session
from pickle import dumps, loads
session = Session("192.168.x.xxx", 8004)
tk_uhfli = tkUHFLI("DEVXXXX", "UHFLI", session)
uhfli = UHFLI(tk_uhfli, session, "uhfli")
uhfli.demods.demods0.adcselect() # no problem
loads(dumps(uhfli.demods.demods0.adcselect())) # pickling error
Right now the check_zsync_connection
function of the PQSC returns immediately if no sync connection is possible.
In these cases it should still block.
The CommanTable serialization is too slow, it takes few tens of milliseconds even for trivial tables
Use the attached script ct_performances.zip
Windows 10 22H2
Hi there,
If you want to get the value of the trigger mode for a demodulator on the UHFLI, a ValueError is raised on an invalid enum member name :
File "C:\Users\XXX\AppData\Local\Temp\ipykernel_12200\1024915319.py", line 1, in <cell line: 1>
uhfli.demods.demods0.trigger()
File "D:\users\XXX\venv-3.9\lib\site-packages\zhinst\qcodes\qcodes_adaptions.py", line 208, in __call__
return self.get(**kwargs)
File "D:\users\XXX\venv-3.9\lib\site-packages\qcodes\instrument\parameter.py", line 650, in get_wrapper
raise e
File "D:\users\XXX\venv-3.9\lib\site-packages\qcodes\instrument\parameter.py", line 637, in get_wrapper
raw_value = get_function(*args, **kwargs)
File "D:\users\XXX\venv-3.9\lib\site-packages\zhinst\toolkit\nodetree\node.py", line 505, in _get
value = self._parse_get_value(value, enum=enum, parse=parse)
File "D:\users\XXX\venv-3.9\lib\site-packages\zhinst\toolkit\nodetree\node.py", line 459, in _parse_get_value
getattr(self.node_info.enum, enum_info.enum)
File "D:\users\XXX\venv-3.9\lib\site-packages\zhinst\toolkit\nodetree\node.py", line 239, in enum
IntEnum(self.path, options_reversed, module=__name__)
File "C:\python\Python39\lib\enum.py", line 386, in __call__
return cls._create_(
File "C:\python\Python39\lib\enum.py", line 510, in _create_
enum_class = metacls.__new__(metacls, class_name, bases, classdict)
File "C:\python\Python39\lib\enum.py", line 207, in __new__
raise ValueError('Invalid enum member name: {0}'.format(
ValueError: ('Invalid enum member name: ', 'getting uhfli_demods0_trigger')
from zhinst.qcodes.driver.devices import UHFLI
from zhinst.toolkit.driver.devices import UHFLI as tkUHFLI
from zhinst.toolkit import Session
session = Session("192.168.x.xxx", 8004)
tk_uhfli = tkUHFLI("DEVXXXX", "UHFLI", session)
uhfli = UHFLI(tk_uhfli, session, "uhfli")
uhfli.demods.demods0.trigger()
If you dive into the code a bit, you can see that in zhinst.toolkit.nodetree.node.NodeInfo.enum, in line 235, the variable options_reversed gives (at least in my case):
{'continuous': 0, '': 128, 'trigin2_falling': 2, 'trigin2_both': 3, 'trigin2or3_rising': 5, 'trigin3_falling': 8, 'trigin2or3_falling': 10, 'trigin3_both': 12, 'trigin2or3_both': 15, 'trigin2_low': 16, 'trigin3_low': 64, 'trigin2or3_low': 80, 'trigin2or3_high': 160, 'trigin0_rising': 1048576, 'trigin1_rising': 2097152, 'trigin2_rising': 4194304, 'trigin3_rising': 8388608, 'trigin0_high': 16777216, 'trigin1_high': 33554432, 'trigin2_high': 67108864, 'trigin3_high': 134217728}
The description of 128 is an empty string and IntEnum won't accept that.
When uploading a sequencer program through toolkit the same awg module gets reused within a session.
This means the settings from the previous upload can effect the current upload. Even though there are not many nodes for the awg module it can cause some problems.
One is for instance that when uploading a sequencer code to a core with the number 8 and than trying to upload a sequencer program to a device with less than 8 cores will raise a ZIAPIServerException. This happens because when changing the device on a AWG module it disables the awg core. If the index is an invalid value it will raise an exception.
hasattr
always returns True
Calling hasattr
on Node
always returns True
hasattr
should return True
only on valid nodes/attributes or implement a more clear way to check child nodes other than child_nodes()
As hasattr
calls getattr
https://docs.python.org/3/library/functions.html#hasattr and Node
always returns a attribute even though it is not valid (as node path validation happens only when a node is called.
Hi,
There appears to be a subtle code aliasing bug in the the read
function scope code in control/drivers/base/scope.py, that manifests itself when you read more than one record from the scope.
The recorded_data
variable (line 225) is defined outside of the for-loop at line 229, where the data from the _read()
call is processed. During each iteration of the for loop, a dictionary with "data" and "time" keys is appended to the result
array. But the "data" key in the dictionary refers to the same (element of) recorded_data
in each iteration. As a result, all entries in the result will have the same values, namely from the last record.
Moving the recorded_data = [[], []]
statement from line 225 to inside the for-loop solves this issue.
I can submit a PR if you would like me to do so.
LabOne does have two kinds of options, if any, for a node.
Named options
e.g. /DEV…./DEMODS/n/ADCSELECT
0: "sigin0", "signal_input0": Sig In 1,
1: "currin0", "current_input0": Curr In 1,
2: "trigin0", "trigger_input0": Trigger Input 1,
3: "trigin1", "trigger_input1": Trigger Input 2,
...
Namless options
e.g. /DEV…./DEMODS/n/ORDER
1: 1st order filter 6 dB/oct,
2: 2nd order filter 12 dB/oct,
3: 3rd order filter 18 dB/oct,
4: 4th order filter 24 dB/oct,
Getting the value of a named one returns the underlying enum. Getting a nameless one is treated as a normal number. So far everything works just fine. However if one tries to access the enum of a namless option node an exception is raised.
from zhinst.toolkit import Session
session = Session("localhost")
device = session.connect_device("dev3036")
device.demods[0].order.node_info.enum
Traceback (most recent call last):
File "", line 1, in
File "/Users/tobiasa/.pyenv/versions/3.10.5/envs/toolkit/lib/python3.10/site-packages/zhinst/toolkit/nodetree/node.py", line 237, in enum
IntEnum(self.path, options_reversed, module=name)
File "/Users/tobiasa/.pyenv/versions/3.10.5/lib/python3.10/enum.py", line 387, in call
return cls.create(
File "/Users/tobiasa/.pyenv/versions/3.10.5/lib/python3.10/enum.py", line 518, in create
enum_class = metacls.new(metacls, class_name, bases, classdict)
File "/Users/tobiasa/.pyenv/versions/3.10.5/lib/python3.10/enum.py", line 208, in new
raise ValueError('Invalid enum member name: {0}'.format(
ValueError: Invalid enum member name:
It looks like HF2LI is not supported by this package, while the other lock-ins are. Is there any reason why this model is omitted?
Toolkit offers a Waveform class that represents a waveform dictionary. Currently it can be used to up-/download waveforms from a device.
For an AWG Core the waveforms must be predefined in the sequencer code to reserve the memory space. This code can be autogenerated from the Waveform
object since all information (length, markers, index, ...) are available.
The proposal is to add a function to generate a seqc snippet to declare waveforms from a Waveform
object. That would reduce duplication of information and reduce error by eventual mismatch of parameters.
Two types are proposed, anonymous and named.
Anonymous waveforms are waveform declared without being associated to a wave object in seqc. They are useful when all the waveform playback is done using the command table, and names are useless or even a complication.
The following waveform object
waveforms = Waveforms()
# Waveform at index 0 with markers
waveforms[0] = (0.5*np.ones(1008), -0.2*np.ones(1008), np.ones(1008))
# Waveform at index 2 without markers
waveforms[2] = (np.random.rand(1008), np.random.rand(1008))
would result in this seqc code snippet
// Waveforms declaration
assignWaveIndex(placeholder(1008, true, false), placeholder(1008, false, false), 0);
assignWaveIndex(placeholder(1008, false, false), placeholder(1008, false, false), 2);
Named waveforms need to be used when the playback is done with playWave. The user must provide a valid identifier in seqc to be assigned to wave object in seqc (Potentially a default name could be provided e.g. incrementing naming w1
, w2
, ...)
The following waveform object
waveforms = Waveforms()
# Waveform at index 0 with markers
waveforms.assign_waveform(
0,
wave1=0.5 * np.ones(1008),
wave1_name="w1",
wave2=-0.2 * np.ones(1008),
wave2_name="w2",
markers=np.ones(1008),
)
# Waveform at index 2 without markers
waveforms.assign_waveform(
0,
wave1=np.random.rand(1008),
wave1_name="w3",
wave2=np.random.rand(1008),
wave2_name="w4",
)
would result in this seqc code snippet
// Waveforms declaration
wave w1 = placeholder(1008, true, false);
wave w2 = placeholder(1008, false, false);
assignWaveIndex(w1, w2, 0);
wave w3 = placeholder(1008, false, false);
wave w4 = placeholder(1008, false, false);
assignWaveIndex(w3, w4, 2);
A mixture of both modes could be allowed.
Waveform
object?Historically, the zhinst.toolkit.Session
requires the host even if an existing connection is passed to it. This was because the host could not be extracted from an existing connection. With 22.08 the zhinst.core.ziDAQServer
class has a property for the host. So, passing the host should be made optional in case a connection is passed.
from zhinst.core import ziDAQServer
from zhinst.toolkit import Session
daq = ziDAQServer("localhost",8004,6)
print(daq.host)
session = Session("localhost", connection=daq)
The HDAWG driver attempts to initialise 4 awg cores when on a 4 channel instrument only 2 exist. The error is
from zhinst.toolkit.control.drivers.hdawg import HDAWG
hdawg=HDAWG('hdawg', 'DEV8162', interface='USB', host='localhost')
hdawg.setup()
hdawg.connect_device()
Successfully connected to data server at localhost:8004 api version: 6
Successfully connected to device DEV8162 on interface USB
ERROR:
The device hdawg (dev8162) does not have the node /dev8162/awgs/2/enable. Please check the node address.
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "C:\Users\ultservi\.conda\envs\qcodes\lib\site-packages\zhinst\toolkit\control\drivers\hdawg.py", line 112, in connect_device
self._init_awg_cores()
File "C:\Users\ultservi\.conda\envs\qcodes\lib\site-packages\zhinst\toolkit\control\drivers\hdawg.py", line 170, in _init_awg_cores
[awg._init_awg_params() for awg in self.awgs]
File "C:\Users\ultservi\.conda\envs\qcodes\lib\site-packages\zhinst\toolkit\control\drivers\hdawg.py", line 170, in <listcomp>
[awg._init_awg_params() for awg in self.awgs]
File "C:\Users\ultservi\.conda\envs\qcodes\lib\site-packages\zhinst\toolkit\control\drivers\hdawg.py", line 329, in _init_awg_params
self._parent._get_node_dict(f"awgs/{self._index}/enable"),
File "C:\Users\ultservi\.conda\envs\qcodes\lib\site-packages\zhinst\toolkit\control\drivers\base\base.py", line 501, in _get_node_dict
self._check_node_exists(device_node)
File "C:\Users\ultservi\.conda\envs\qcodes\lib\site-packages\zhinst\toolkit\control\drivers\base\base.py", line 546, in _check_node_exists
_logger.ExceptionTypes.ToolkitError,
File "C:\Users\ultservi\.conda\envs\qcodes\lib\site-packages\zhinst\toolkit\interface\interface.py", line 192, in error
raise LoggerModule.ToolkitError(msg)
zhinst.toolkit.interface.interface.ToolkitError: The device hdawg (dev8162) does not have the node /dev8162/awgs/2/enable. Please check the node address.
A quick fix for this is to change the number in _init_awg_cores
from 4 to 2, but is there a more permanent solution that won't require editing the driver with each update?
This is also an issue with the zhinst-qcodes driver and requires the fix in both the toolkit and qcodes versions
def _init_awg_cores(self):
"""Initialize the AWGs cores of the device."""
self._awgs = [AWG(self, i) for i in range(4)] # Change to 2
[awg._setup() for awg in self.awgs]
[awg._init_awg_params() for awg in self.awgs]
[awg._init_ct() for awg in self.awgs]
zhinst-toolkit already offers a Waveform
and CommanTable
class that eases the use of these to structures. It would be great to have something similar for the sequencer code itself. The feature set can be expanded over time but the following feature are the initial proposal:
{
in the hole code)Waveform
object.The SequencerCode
object could be passed directly to the load_sequencer_program function. Toolkit then automatically adds a section to the beginning of the sequencer code for constants and the Waveform definitions.
Example:
waveforms = Waveforms()
waveforms[0] = (0.5*np.ones(1008), -0.2*np.ones(1008), np.ones(1008))
sequencer = SequencerCode()
sequencer.constants["PULSE_WIDTH"] = 10e-9 #ns
sequencer.waveforms = waveforms
sequencer.code = """\
// Hello World
repeat(5)
...
"""
The resulting sequencer code would be then something like:
// Constants
const PULSE_WIDTH = 10e-9;
// Waveforms declaration
assignWaveIndex(placeholder(1008, true, false), placeholder(1008, false, false), 0);
assignWaveIndex(placeholder(1008, false, false), placeholder(1008, false, false), 2);
// Hello World
repeat(5)
...
blocked by #140
According to the documentation when parameters in a custom awg sequence should be written:
$paramN$
where the index N begins from 0.
However, reading the source here
def update_params(self): if self.path: self.program = Path(self.path).read_text() for i, p in enumerate(self.custom_params): self.program = self.program.replace(f"$param{i+1}$", str℗)
it appears that the index in the .seqc file should start from 1. This also seems to work in practice.
The compile_sequence_program function raises an error when we try to play a wave on channel 3 or 4, even when the channel grouping is set to 1x4.
Traceback (most recent call last): File "C:\Users\Dr C Mitra\Downloads\AWG_INTERFACE_PYTHON.py", line 35, in <module> elf_file, info = device.awgs[0].compile_sequencer_program(awg_program) File "C:\Users\Dr C Mitra\AppData\Local\Programs\Python\Python39\lib\site-packages\zhinst\toolkit\driver\nodes\awg.py", line 136, in compile_sequencer_program return compile_seqc( zhinst.core.errors.CoreError: Compilation failed: Compiler Error (line: 6): can't play channel 3, valid values for channel are from 1 to 2
`from zhinst.toolkit import Session
import numpy as np
session = Session('localhost')
device = session.connect_device("DEV8369")
OUT_CHANNEL = 0
AWG_CHANNEL = 0
device.system.awg.channelgrouping(1)
print(device.awgs)
with device.set_transaction():
#device.sigouts[OUT_CHANNEL].on(True)
device.sigouts[OUT_CHANNEL].range(1)
device.sigouts[OUT_CHANNEL+1].range(1)
device.awgs[1].outputs[AWG_CHANNEL].amplitude(0.5)
#device.awgs[0].outputs[AWG_CHANNEL].modulation.mode(0)
#device.awgs[0].time(0)
#device.awgs[0].userregs(0)
from zhinst.toolkit import Sequence
AWG_N = 2000
awg_program = Sequence()
awg_program.constants["AWG_N"] = AWG_N
awg_program.code = """
wave w0 = gauss(AWG_N, AWG_N/2, AWG_N/20);
while(getUserReg(0) == 0) {
playWave(3,w0);
}
"""
print(awg_program)
elf_file, info = device.awgs[0].compile_sequencer_program(awg_program)
print(info)
session.disconnect_device("DEV8369")`
4 channel HDAWG 750 MHz
The device.system.awg.channelgrouping function seems to be working properly as has been verified by running the code and observing the changes in the LabOne interface. It seems that the offline compiler which is used by the compile_sequence_program function doesn't take this change into account. We are facing a similar problem with the load_sequence_program function.
Add mypy
(Added in #125) to check code type hints.
Please feel free to pick a file, make mypy
pass and create a PR.
Once finished: Create PR and remove the file from setup.cfg [mypy]
-section
The following files not fully covered by passing checks:
In src/zhinst/toolkit/resources/ct_schema_hdawg.json
, all boolean value entries with default values are give a string default value, such as "false"
, which quotation marks should not be there. It is not checked if this is also present on other JSON schema files.
Toolkit raises an KeyError when calling connect_device
on Session.
This happens when there are parsers defined for a node which does not exists in the instrument (old firmware version).
Device the problem occured on: SHFSG
from zhinst.toolkit import Session
session = Session('localhost")
device = session.connect_device('devxxxxx')
Error message:
File "C:\Users\markush\Documents\zhinst\zhinst-toolkit\venv\lib\site-packages\zhinst\toolkit\session.py", line 76, in _create_device
return self._device_classes.get(dev_type, tk_devices.BaseInstrument)(
File "C:\Users\markush\Documents\zhinst\zhinst-toolkit\venv\lib\site-packages\zhinst\toolkit\driver\devices\base.py", line 73, in __init__
nodetree.update_nodes(node_parser.get(self.__class__.__name__, {}))
File "C:\Users\markush\Documents\zhinst\zhinst-toolkit\venv\lib\site-packages\zhinst\toolkit\nodetree\nodetree.py", line 321, in update_nodes
self.update_node(node, updates, add=add)
File "C:\Users\markush\Documents\zhinst\zhinst-toolkit\venv\lib\site-packages\zhinst\toolkit\nodetree\nodetree.py", line 284, in update_node
raise KeyError(potential_key)
KeyError: '/dev12029/sgchannels/*/awg/outputs/*/enables/*'
A proper error message should be put out to notice the caller to update the device.
Sphinx
0.5.0 and pydata-theme 0.9.0
breaks custom formatting
Related commit: 03d9c92
Install latest sphinx and pydata theme and generate docs
Sphinx build complains about the missing references.
Example WARNING when building docs:
WARNING: Cannot resolve forward reference in type annotations of "zhinst.toolkit.driver.devices.shfqa.QAChannel": name 'Session' is not defined
This is probably due to type hinting.
if t.TYPE_HINTING:
from zhinst.toolkit.session import Session
and using it in as type hint in, for example BaseInstrument
:
def __init__(
self,
serial: str,
device_type: str,
session: "Session", <----
):
This could be probably be solved importing the whole zhinst.toolkit.session
module
https://peps.python.org/pep-0484/
https://github.com/tox-dev/sphinx-autodoc-typehints#dealing-with-circular-imports
So it would result to:
from __future__ import annotations
import zhinst.toolkit.session
and using it in as type hint in, for example BaseInstrument
:
def __init__(
self,
serial: str,
device_type: str,
session: zhinst.toolkit.session.Session
):
Current release is set to 0.1.5 on pypi, but it will install 0.1.2
None of the new functionality works that is in the top level master branch which I would assume is the current release
For instance only three options exist in zhinst.toolkit.helers.utils for Trigger mode from the pip install, but there are several available through the current version, which then conflicts with the ZI provided driveres
The install needs to be fixed.
Rename GitHub action "CI" to "tests"
A code like this will not work and throw a cryptic error
for awg_core in np.arange(2):
#awg_core = int(awg_core) #WORKAROUND
device.awgs[awg_core].write_to_waveform_memory(waveforms)
The reason is that awg_core
is a numpy.int64 integer, instead of a regular Python integer. As workaround, casting it to int make it working
If an empty source string is passed to the AWG module it does not start the compilation
The function awg.load_sequencer_program waits until the compiler flag indicates a successful compilation. This will always timeout with an empty string.
We should document that empty strings are not allowed and probably raise a Runtime Exception. (Not raising anything would be dangerous since it would indicate a successful upload.
TBD
Deprecated commands in Sequence program examples as of Zurich Instruments LabOne 22.02:
Replaced by StartQA-command.
Examples:
uhfqa_result_unit.md
When accessing nodes through attributes capital letters causes the node not to be recognized. This is due to the fact that the internal dictionary is all lower case.
It should be possible to access node independent of the casing.
from zhinst.toolkit import Session
session = Session("localhost")
print(session.debug.level())
print(session.debug.Level())
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.