Skip to content

Commit

Permalink
Improve write_to_waveform_memory
Browse files Browse the repository at this point in the history
* Append to existing transactions
* Add a flag to disable local santity check
  • Loading branch information
tobiasah committed Jul 7, 2022
1 parent 2c44da0 commit 7f1d50c
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 50 deletions.
7 changes: 5 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
# zhinst-toolkit Changelog
## Version 0.3.5
* Adapt AWG Waveform upload (`write_to_waveform_memory`) to append to existing transactions.
* Make consistency validate during waveform upload optional (new flag `validate` in `write_to_waveform_memory`).

## Version 0.3.4

* `Commandtable.load_validation_schema` can also get the command table
JSON schema from the device.
* ``load_sequencer_program`` now raises an ``ValueError``
* ``load_sequencer_program`` now raises an ``ValueError``
if empty ``sequencer_program`` string is given. :issue:`138`
* Added a new `raise_for_invalid_node` keyword-argument to ``NodeTree.update_nodes``. :issue:`110`

Now only a warning (instead of ``KeyError``) is issued when trying to initialize device/module object, which does
not have nodes defined in node value parsers.

Expand Down
80 changes: 49 additions & 31 deletions src/zhinst/toolkit/driver/nodes/awg.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@

from zhinst.toolkit.driver.nodes.command_table_node import CommandTableNode
from zhinst.toolkit.nodetree import Node, NodeTree
from zhinst.toolkit.nodetree.helper import lazy_property
from zhinst.toolkit.nodetree.helper import (
lazy_property,
create_or_append_set_transaction,
)
from zhinst.toolkit.waveform import Waveforms

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -167,47 +170,60 @@ def load_sequencer_program(
)

def write_to_waveform_memory(
self, waveforms: Waveforms, indexes: list = None
self, waveforms: Waveforms, indexes: list = None, validate: bool = True
) -> None:
"""Writes waveforms to the waveform memory.
The waveforms must already be assigned in the sequencer program.
Args:
waveforms: Waveforms that should be uploaded.
indexes: Specify a list of indexes that should be uploaded. If
nothing is specified all available indexes in waveforms will
be uploaded. (default = None)
validate: Enable sanity check preformed by toolkit, based on the
waveform descriptors on the device. Can be disabled for e.g.
speed optimizations. Does not affect the checks happen in LabOne
and or the firmware. (default = True)
Raises:
IndexError: The index of a waveform exceeds the one on the device
RuntimeError: One of the waveforms index points to a filler(placeholder)
and `validate` is True.
RuntimeError: One of the waveforms index points to a
filler(placeholder) and `validate` is True.
"""
waveform_info = json.loads(self.waveform.descriptors()).get("waveforms", [])
num_waveforms = len(waveform_info)
commands = []
for waveform_index in waveforms.keys():
if indexes and waveform_index not in indexes:
continue
if waveform_index >= num_waveforms:
raise IndexError(
f"There are {num_waveforms} waveforms defined "
"on the device but the passed waveforms specified one with index "
f"{waveform_index}."
)
if "__filler" in waveform_info[waveform_index]["name"]:
raise RuntimeError(
f"The waveform at index {waveform_index} is only "
"a filler and can not be overwritten"
)

commands.append(
(
self.waveform.node_info.path + f"/waves/{waveform_index}",
waveform_info = None
num_waveforms = None
if validate:
waveform_info = json.loads(self.waveform.descriptors()).get("waveforms", [])
num_waveforms = len(waveform_info)
with create_or_append_set_transaction(self._root):
for waveform_index in waveforms.keys():
if indexes and waveform_index not in indexes:
continue
if num_waveforms is not None and waveform_index >= num_waveforms:
raise IndexError(
f"There are {num_waveforms} waveforms defined on the device "
"but the passed waveforms specified one with index "
f"{waveform_index}."
)
if (
waveform_info
and "__filler" in waveform_info[waveform_index]["name"]
):
raise RuntimeError(
f"The waveform at index {waveform_index} is only "
"a filler and can not be overwritten"
)
self.root.transaction.add(
self.waveform.waves[waveform_index],
waveforms.get_raw_vector(
waveform_index,
target_length=int(waveform_info[waveform_index]["length"]),
target_length=int(waveform_info[waveform_index]["length"])
if waveform_info
else None,
),
)
)
self._session.daq_server.set(commands)

def read_from_waveform_memory(self, indexes: t.List[int] = None) -> Waveforms:
"""Read waveforms to the waveform memory.
Expand All @@ -217,19 +233,21 @@ def read_from_waveform_memory(self, indexes: t.List[int] = None) -> Waveforms:
specified all assigned waveforms will be downloaded.
Returns:
Mutable mapping of the downloaded waveforms.
Waveform object with the downloaded waveforms.
"""
waveform_info = json.loads(self.waveform.descriptors()).get("waveforms", [])
nodes = []
nodes = [self.waveform.descriptors.node_info.path]
if indexes is not None:
for index in indexes:
nodes.append(self.waveform.node_info.path + f"/waves/{index}")
else:
nodes.append(self.waveform.node_info.path + "/waves/*")
nodes.append(self.waveform.waves["*"].node_info.path)
nodes = ",".join(nodes)
waveforms_raw = self._session.daq_server.get(
nodes, settingsonly=False, flat=True
)
waveform_info = json.loads(
waveforms_raw.pop(self.waveform.descriptors.node_info.path)[0]["vector"]
).get("waveforms", [])
waveforms = Waveforms()
for node, waveform in waveforms_raw.items():
slot = int(node[-1])
Expand Down
69 changes: 52 additions & 17 deletions tests/test_awg.py
Original file line number Diff line number Diff line change
Expand Up @@ -239,15 +239,35 @@ def test_write_to_waveform_memory(
)
assert len(mock_connection.return_value.set.call_args[0][0]) == 1

# existing transaction
with awg_module.root.set_transaction():
awg_module.dio.highbits(0)
awg_module.write_to_waveform_memory(waveforms)

mock_connection.return_value.set.call_args[0][0][0][
0
] == "/dev1234/sgchannels/0/awg/dio/highbits"
assert len(mock_connection.return_value.set.call_args[0][0]) == 3

# to big index
waveforms[10] = (wave1, wave2)
with pytest.raises(IndexError) as e_info:
awg_module.write_to_waveform_memory(waveforms)
awg_module.write_to_waveform_memory(waveforms, validate=False)
assert (
mock_connection.return_value.set.call_args[0][0][2][0]
== "/dev1234/sgchannels/0/awg/waveform/waves/10"
)
del waveforms[10]
# assign to filler
waveforms[2] = (wave1, wave2)
with pytest.raises(RuntimeError) as e_info:
awg_module.write_to_waveform_memory(waveforms)
awg_module.write_to_waveform_memory(waveforms, validate=False)
assert (
mock_connection.return_value.set.call_args[0][0][2][0]
== "/dev1234/sgchannels/0/awg/waveform/waves/2"
)


def test_read_from_waveform_memory(
Expand All @@ -258,25 +278,20 @@ def test_read_from_waveform_memory(
single_wave_result = []

def get_side_effect(nodes, **kwargs):
if nodes.lower() == "/dev1234/sgchannels/0/awg/waveform/descriptors":
return OrderedDict(
[
(
"/dev1234/sgchannels/0/awg/waveform/descriptors",
[
{
"timestamp": 1158178198389432,
"flags": 0,
"vector": json.dumps(waveform_descriptiors),
}
],
)
]
)
if "/dev1234/sgchannels/0/awg/waveform/waves/" in nodes.lower():
if nodes[-1] == "*":
return OrderedDict(
[
(
"/dev1234/sgchannels/0/awg/waveform/descriptors",
[
{
"timestamp": 1158178198389432,
"flags": 0,
"vector": json.dumps(waveform_descriptiors),
}
],
),
(
"/dev1234/sgchannels/0/awg/waveform/waves/0",
[
Expand Down Expand Up @@ -314,6 +329,16 @@ def get_side_effect(nodes, **kwargs):
else:
return OrderedDict(
[
(
"/dev1234/sgchannels/0/awg/waveform/descriptors",
[
{
"timestamp": 1158178198389432,
"flags": 0,
"vector": json.dumps(waveform_descriptiors),
}
],
),
(
f"/dev1234/sgchannels/0/awg/waveform/waves/{nodes[-1]}",
[
Expand All @@ -323,25 +348,35 @@ def get_side_effect(nodes, **kwargs):
"vector": single_wave_result,
}
],
)
),
]
)
raise RuntimeError()

mock_connection.return_value.get.side_effect = get_side_effect
waveforms = awg_module.read_from_waveform_memory()
mock_connection.return_value.get.assert_called_with(
"/dev1234/sgchannels/0/awg/waveform/descriptors,/dev1234/sgchannels/0/awg/waveform/waves/*",
settingsonly=False,
flat=True,
)
assert all(waveforms[0][0] == np.ones(1008))
assert all(waveforms[0][1] == -np.ones(1008))
assert all(waveforms[0][2] == np.ones(1008))
assert all(waveforms[1][0] == np.ones(0))
assert all(waveforms[1][1] == np.ones(0))
assert all(waveforms[1][2] == np.ones(0))

# single Node Acces
# single Node Access
single_wave_result = zi_utils.convert_awg_waveform(
np.ones(1008), -np.ones(1008), np.ones(1008)
)
waveforms = awg_module.read_from_waveform_memory([0])
mock_connection.return_value.get.assert_called_with(
"/dev1234/sgchannels/0/awg/waveform/descriptors,/dev1234/sgchannels/0/awg/waveform/waves/0",
settingsonly=False,
flat=True,
)
assert len(waveforms) == 1
assert all(waveforms[0][0] == np.ones(1008))
assert all(waveforms[0][1] == -np.ones(1008))
Expand Down

0 comments on commit 7f1d50c

Please sign in to comment.