Giter VIP home page Giter VIP logo

zhinst-toolkit's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

zhinst-toolkit's Issues

Add GitLab CI to run tests on supported Python versions

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.

Impossible to create a single transaction for two devices connected to the same DataServer

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"))

Block functions that enable and check the status of unit in transactional set

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

@tobiasah

Wrong _device_type of awg node of UHFQA/UHFLI

Description

The device type of awg node in UHFQA is 0 rather than the name string

How to reproduce

qa = session.connect_device("dev2571")
print(qa.awgs[0]._device_type)

Environment info

  • Python version: 3.8.5
  • zhinst-toolkit version: 0.4.1
  • LabOne version: 22.2.29711
  • zhinst version: 22.2.29711

Device/system information (if applicable)

UHFQA

Additional information

It seems this bug is due to wrong argument in uhfli.py, the index is feed to the device type when initializing awg node.

HDAWG initialization fails because of command table schema retrieval

With the 0.2.2 version of zhinst-toolkit, the CommandTable was introduced. This causes problems in our setup:

  • The zhinst server responds with HTTP error 403: Forbidden
  • Apart from that, the current code would mean that the HDAWG cannot be used without a functioning internet connection, or if for whatever reason the zhinst server is not available.

The first problem can easily be demonstrated:

  • Open IDLE (Python 3.9.7 on Windows in my case), and execute the following:
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.

wait_for_state_change does not work correctly with wildcards

Description

    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:




sync keyword missing in the _set method of zhinst.toolkit.control.drivers.base.daq.DAQModule

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

Cloudflare is blocking third party documentation builds that use intersphinx

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?


How to reproduce

Environment info

  • Python version:
  • zhinst-toolkit version:
  • LabOne version:
  • zhinst version:

Device/system information (if applicable)

Additional information

HDAWG4 | Allowed number of channels limited to 2 in grouping mode (1x4)

Description

What I want to do:

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:

  1. Play the control pulse of time 't' on outputs 1 and 2.
  2. Play readout pulse after step 1 is finished on 3 and 4.
  3. Wait for 1000 clock cycles, increment t-> t+dt, and go back to step 1.

Problem:

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.

How to reproduce:

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].


Environment info

  • Python version: 3.10
  • zhinst-toolkit version: 0.5.2
  • LabOne version: 23.02
  • zhinst version: 23.2.2

Device/system information (if applicable)

Using HDAWG4

Additional information

Enum values cannot be pickled

Description

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.

How to reproduce

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

Environment info

  • Python version: 3.9.7
  • zhinst-toolkit version: 0.3.3
  • LabOne version: 22.02.29711
  • zhinst version: 22.2.29711

Device/system information (if applicable)

Additional information

The CT serialization is slow

Description

The CommanTable serialization is too slow, it takes few tens of milliseconds even for trivial tables

How to reproduce

Use the attached script ct_performances.zip

Environment info

  • Python version: 3.11.5
  • zhinst-toolkit version: main
  • LabOne version: not relevant (no instrument connection)
  • zhinst version: not relevant (no instrument connection)

Device/system information (if applicable)

Windows 10 22H2

Additional information

Getting trigger from uhfli demod raises ValueError on trying empty string enum member name

Description

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')

How to reproduce

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.

Environment info

  • Python version: 3.9.7
  • zhinst-toolkit version: 0.3.3
  • LabOne version: 22.02.29711
  • zhinst version: 22.2.29711

Device/system information (if applicable)

Additional information

Uploading sequencer code to two different devices in a row can cause a ZIAPIServerException

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.

Calling `hasattr` on `Node` always returns `True`

Description

hasattr always returns True

How to reproduce

Calling hasattr on Node always returns True

Environment info

  • Python version:
  • zhinst-toolkit version:
  • LabOne version:
  • zhinst version:

Device/system information (if applicable)

Additional information

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.

Multi-record scope read repeats final record

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.

Trying to get the enum for a node with nameless options raises exception.

Description

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.

How to reproduce

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:

Environment info

  • Python version: 3.10
  • zhinst-toolkit version: master (3.5)
  • LabOne version: 22.02
  • zhinst version: 22.02

Device/system information (if applicable)

Additional information

HF2LI support

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?

Generate sequencer code from `Waveform` class

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

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

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.

Open Questions

  • Best way to pass names to Waveform object?
  • Automated naming usefull?
  • SHFQA / Generator handling?
  • Does the generated code need to look different for different device types?

zhinst.toolkit.Session requires host even if an existing connection is passed.

Description

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.

How to reproduce

from zhinst.core import ziDAQServer
from zhinst.toolkit import Session

daq = ziDAQServer("localhost",8004,6)
print(daq.host)
session = Session("localhost", connection=daq)

4 channel HDAWG

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]

Introduce Sequencer Code class

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:

  • Define Constants (avoid the need of f-strings in the sequencer code which enforces escaping { in the hole code)
  • Link a 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)
...

Benefits

  • Simple, save, reproducible creation of sequencer code
  • Can be used offline
  • Sequencer code can be shared easily between different devices
  • Ease interface to add new feature to the sequencer code in the future

blocked by #140

Incorrect documentation for custom awg sequence

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.

compile_sequence_program doesn't work properly for 1x4 channel grouping

Description

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

How to reproduce

`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")`

Environment info

  • Python version: 3.9.7
  • zhinst-toolkit version: 0.4.1
  • LabOne version: 22.08.35048
  • zhinst version: 22.8.1

Device/system information (if applicable)

4 channel HDAWG 750 MHz

Additional information

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.

Mypy type annotations

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:

  • src/zhinst/toolkit/nodetree/helper.py #159
  • src/zhinst/toolkit/session.py #164
  • src/zhinst/toolkit/waveform.py #139
  • src/zhinst/toolkit/command_table.py #164
  • src/zhinst/toolkit/nodetree/nodetree.py #164
  • src/zhinst/toolkit/nodetree/node.py #164
  • src/zhinst/toolkit/nodetree/connection_dict.py #164
  • src/zhinst/toolkit/driver/modules/base_module.py #159
  • src/zhinst/toolkit/driver/modules/shfqa_sweeper.py #164
  • src/zhinst/toolkit/driver/modules/daq_module.py #159
  • src/zhinst/toolkit/driver/modules/sweeper_module.py #159
  • src/zhinst/toolkit/driver/devices/uhfqa.py #164
  • src/zhinst/toolkit/driver/devices/hdawg.py #159
  • src/zhinst/toolkit/driver/devices/base.py #159
  • src/zhinst/toolkit/driver/nodes/readout.py #159
  • src/zhinst/toolkit/driver/nodes/spectroscopy.py #159
  • src/zhinst/toolkit/driver/nodes/command_table_node.py #159
  • src/zhinst/toolkit/driver/nodes/generator.py #159
  • src/zhinst/toolkit/driver/nodes/awg.py #159

JSON schema provides wrong default value type on boolean values

Description

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.

How to reproduce

Environment info

  • Python version:
  • zhinst-toolkit version:
  • LabOne version:
  • zhinst version:

Device/system information (if applicable)

Additional information

KeyError when calling session.connect_device()

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

Description

Sphinx 0.5.0 and pydata-theme 0.9.0 breaks custom formatting

  • Bullet points are not generated and the font is a little bit lighter.

Related commit: 03d9c92

How to reproduce

Install latest sphinx and pydata theme and generate docs

Environment info

  • Python version:
  • zhinst-toolkit version:
  • LabOne version:
  • zhinst version:

Device/system information (if applicable)

Additional information

`Sphinx`: WARNING: Cannot resolve forward reference in type annotations of ...

Description

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
):

How to reproduce

Environment info

  • Python version:
  • zhinst-toolkit version:
  • LabOne version:
  • zhinst version:

Device/system information (if applicable)

Additional information

pip install installs wrong version of zhinst

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.

Some toolkit function don't work when a feature is indexed with a numpy integer

Description

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

How to reproduce

Environment info

  • Python version:
  • zhinst-toolkit version:
  • LabOne version:
  • zhinst version:

Device/system information (if applicable)

Additional information

awg.load_sequencer_program times out if the source string is empty

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.

Deprecation warning in Sequence program examples

Deprecated commands in Sequence program examples as of Zurich Instruments LabOne 22.02:

  • AWG_MONITOR_TRIGGER
  • AWG_INTEGRATION_ARM
  • AWG_INTEGRATION_TRIGGER

Replaced by StartQA-command.

Examples:

  • uhfqa_result_unit.md

Capital nodes as argument are not supported

Description

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.

How to reproduce

from zhinst.toolkit import Session

session = Session("localhost")
print(session.debug.level())
print(session.debug.Level())

Environment info

  • Python version: 3.10.6
  • zhinst-toolkit version: 3.6
  • LabOne version: 22.02
  • zhinst version: 22.02

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.