PyIBIS-AMI 9.1.0__tar.gz → 9.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pyibis_ami-9.1.0/src/pyibis_ami.egg-info → pyibis_ami-9.3.0}/PKG-INFO +17 -14
- {pyibis_ami-9.1.0 → pyibis_ami-9.3.0}/pyproject.toml +24 -14
- {pyibis_ami-9.1.0 → pyibis_ami-9.3.0/src/pyibis_ami.egg-info}/PKG-INFO +17 -14
- pyibis_ami-9.3.0/src/pyibis_ami.egg-info/requires.txt +15 -0
- {pyibis_ami-9.1.0 → pyibis_ami-9.3.0}/src/pyibisami/ami/config.py +4 -5
- {pyibis_ami-9.1.0 → pyibis_ami-9.3.0}/src/pyibisami/ami/model.py +14 -14
- {pyibis_ami-9.1.0 → pyibis_ami-9.3.0}/src/pyibisami/ami/parameter.py +68 -1
- {pyibis_ami-9.1.0 → pyibis_ami-9.3.0}/src/pyibisami/ami/parser.py +157 -22
- {pyibis_ami-9.1.0 → pyibis_ami-9.3.0}/src/pyibisami/ami/reserved_parameter_names.py +1 -1
- {pyibis_ami-9.1.0 → pyibis_ami-9.3.0}/src/pyibisami/ibis/file.py +29 -29
- {pyibis_ami-9.1.0 → pyibis_ami-9.3.0}/src/pyibisami/ibis/model.py +5 -5
- {pyibis_ami-9.1.0 → pyibis_ami-9.3.0}/src/pyibisami/ibis/parser.py +5 -4
- {pyibis_ami-9.1.0 → pyibis_ami-9.3.0}/src/pyibisami/testing/ami_test_config.py +3 -3
- {pyibis_ami-9.1.0 → pyibis_ami-9.3.0}/src/pyibisami/testing/ami_tests.py +14 -13
- {pyibis_ami-9.1.0 → pyibis_ami-9.3.0}/src/pyibisami/testing/ami_tests_helpers.py +2 -2
- {pyibis_ami-9.1.0 → pyibis_ami-9.3.0}/src/pyibisami/testing/ibis_file_tests.py +3 -3
- {pyibis_ami-9.1.0 → pyibis_ami-9.3.0}/src/pyibisami/testing/test_defs.py +3 -2
- {pyibis_ami-9.1.0 → pyibis_ami-9.3.0}/src/pyibisami/testing/test_models.py +0 -0
- {pyibis_ami-9.1.0 → pyibis_ami-9.3.0}/src/pyibisami/testing/util.py +1 -1
- {pyibis_ami-9.1.0 → pyibis_ami-9.3.0}/src/pyibisami/tools/run_notebook.py +5 -7
- {pyibis_ami-9.1.0 → pyibis_ami-9.3.0}/src/pyibisami/util/plot.py +4 -4
- {pyibis_ami-9.1.0 → pyibis_ami-9.3.0}/src/pyibisami/util/reportlab.py +2 -2
- pyibis_ami-9.1.0/src/pyibis_ami.egg-info/requires.txt +0 -13
- {pyibis_ami-9.1.0 → pyibis_ami-9.3.0}/LICENSE +0 -0
- {pyibis_ami-9.1.0 → pyibis_ami-9.3.0}/MANIFEST.in +0 -0
- {pyibis_ami-9.1.0 → pyibis_ami-9.3.0}/README.md +0 -0
- {pyibis_ami-9.1.0 → pyibis_ami-9.3.0}/docs/source/ami.rst +0 -0
- {pyibis_ami-9.1.0 → pyibis_ami-9.3.0}/docs/source/conf.py +0 -0
- {pyibis_ami-9.1.0 → pyibis_ami-9.3.0}/docs/source/ibis.rst +0 -0
- {pyibis_ami-9.1.0 → pyibis_ami-9.3.0}/docs/source/index.rst +0 -0
- {pyibis_ami-9.1.0 → pyibis_ami-9.3.0}/docs/source/install.rst +0 -0
- {pyibis_ami-9.1.0 → pyibis_ami-9.3.0}/docs/source/modules.rst +0 -0
- {pyibis_ami-9.1.0 → pyibis_ami-9.3.0}/docs/source/tools.rst +0 -0
- {pyibis_ami-9.1.0 → pyibis_ami-9.3.0}/examples/all_taps.run +0 -0
- {pyibis_ami-9.1.0 → pyibis_ami-9.3.0}/examples/bandwidth.run +0 -0
- {pyibis_ami-9.1.0 → pyibis_ami-9.3.0}/examples/dummy.em +0 -0
- {pyibis_ami-9.1.0 → pyibis_ami-9.3.0}/examples/examples.md +0 -0
- {pyibis_ami-9.1.0 → pyibis_ami-9.3.0}/examples/freq_resp.em +0 -0
- {pyibis_ami-9.1.0 → pyibis_ami-9.3.0}/examples/impulse.em +0 -0
- {pyibis_ami-9.1.0 → pyibis_ami-9.3.0}/examples/impulse_response_8ma.txt +0 -0
- {pyibis_ami-9.1.0 → pyibis_ami-9.3.0}/examples/mode.run +0 -0
- {pyibis_ami-9.1.0 → pyibis_ami-9.3.0}/examples/stratix4_ami_tx_runs/all_taps.run +0 -0
- {pyibis_ami-9.1.0 → pyibis_ami-9.3.0}/examples/stratix4_ami_tx_runs/test_results.xml +0 -0
- {pyibis_ami-9.1.0 → pyibis_ami-9.3.0}/examples/test_results/test_results.xsl +0 -0
- {pyibis_ami-9.1.0 → pyibis_ami-9.3.0}/setup.cfg +0 -0
- {pyibis_ami-9.1.0 → pyibis_ami-9.3.0}/src/pyibis_ami.egg-info/SOURCES.txt +0 -0
- {pyibis_ami-9.1.0 → pyibis_ami-9.3.0}/src/pyibis_ami.egg-info/dependency_links.txt +0 -0
- {pyibis_ami-9.1.0 → pyibis_ami-9.3.0}/src/pyibis_ami.egg-info/entry_points.txt +0 -0
- {pyibis_ami-9.1.0 → pyibis_ami-9.3.0}/src/pyibis_ami.egg-info/top_level.txt +0 -0
- {pyibis_ami-9.1.0 → pyibis_ami-9.3.0}/src/pyibisami/IBIS_AMI_Checker.ipynb +0 -0
- {pyibis_ami-9.1.0 → pyibis_ami-9.3.0}/src/pyibisami/IBIS_AMI_Tester.ipynb +0 -0
- {pyibis_ami-9.1.0 → pyibis_ami-9.3.0}/src/pyibisami/__init__.py +0 -0
- {pyibis_ami-9.1.0 → pyibis_ami-9.3.0}/src/pyibisami/__main__.py +0 -0
- {pyibis_ami-9.1.0 → pyibis_ami-9.3.0}/src/pyibisami/ami/__init__.py +0 -0
- {pyibis_ami-9.1.0 → pyibis_ami-9.3.0}/src/pyibisami/ami/generic.ami.em +0 -0
- {pyibis_ami-9.1.0 → pyibis_ami-9.3.0}/src/pyibisami/ami/generic.ibs.em +0 -0
- {pyibis_ami-9.1.0 → pyibis_ami-9.3.0}/src/pyibisami/common.py +0 -0
- {pyibis_ami-9.1.0 → pyibis_ami-9.3.0}/src/pyibisami/ibis/__init__.py +0 -0
- {pyibis_ami-9.1.0 → pyibis_ami-9.3.0}/src/pyibisami/testing/__init__.py +0 -0
- {pyibis_ami-9.1.0 → pyibis_ami-9.3.0}/src/pyibisami/tools/__init__.py +0 -0
- {pyibis_ami-9.1.0 → pyibis_ami-9.3.0}/src/pyibisami/tools/run_tests.py +0 -0
- {pyibis_ami-9.1.0 → pyibis_ami-9.3.0}/src/pyibisami/tools/test_results.xsl +0 -0
- {pyibis_ami-9.1.0 → pyibis_ami-9.3.0}/src/pyibisami/util/ami.py +0 -0
- {pyibis_ami-9.1.0 → pyibis_ami-9.3.0}/src/pyibisami/util/misc.py +0 -0
- {pyibis_ami-9.1.0 → pyibis_ami-9.3.0}/tests/test_ami_test_config.py +0 -0
- {pyibis_ami-9.1.0 → pyibis_ami-9.3.0}/tests/test_run_tests.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pyibis-ami
|
|
3
|
-
Version: 9.
|
|
3
|
+
Version: 9.3.0
|
|
4
4
|
Summary: Facilitates working directly with IBIS-AMI DLLs from the Python command prompt.
|
|
5
5
|
Author-email: David Banas <capn.freako@gmail.com>
|
|
6
6
|
Project-URL: Documentation, https://pyibis-ami.readthedocs.io/en/latest/
|
|
@@ -17,25 +17,28 @@ Classifier: Programming Language :: Python :: 3
|
|
|
17
17
|
Classifier: Programming Language :: Python :: 3.10
|
|
18
18
|
Classifier: Programming Language :: Python :: 3.11
|
|
19
19
|
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
21
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
21
22
|
Classifier: Topic :: System :: Emulators
|
|
22
23
|
Classifier: Topic :: Utilities
|
|
23
|
-
Requires-Python: <3.
|
|
24
|
+
Requires-Python: <3.14,>=3.10
|
|
24
25
|
Description-Content-Type: text/markdown
|
|
25
26
|
License-File: LICENSE
|
|
26
|
-
Requires-Dist: chaco
|
|
27
|
-
Requires-Dist: click
|
|
28
|
-
Requires-Dist: empy
|
|
29
|
-
Requires-Dist: importlib_resources
|
|
30
|
-
Requires-Dist: jupyter
|
|
31
|
-
Requires-Dist: lxml-html-clean
|
|
27
|
+
Requires-Dist: chaco==6.1.1
|
|
28
|
+
Requires-Dist: click==8.4.2
|
|
29
|
+
Requires-Dist: empy==4.2.1
|
|
30
|
+
Requires-Dist: importlib_resources==7.1.0
|
|
31
|
+
Requires-Dist: jupyter==1.1.1
|
|
32
|
+
Requires-Dist: lxml-html-clean==0.4.5
|
|
32
33
|
Requires-Dist: matplotlib>=3.6.1
|
|
33
|
-
Requires-Dist: numpy
|
|
34
|
-
Requires-Dist: papermill
|
|
35
|
-
Requires-Dist: parsec
|
|
36
|
-
Requires-Dist: reportlab
|
|
37
|
-
Requires-Dist: scipy>=1.
|
|
38
|
-
Requires-Dist:
|
|
34
|
+
Requires-Dist: numpy>=1.26
|
|
35
|
+
Requires-Dist: papermill==2.7.0
|
|
36
|
+
Requires-Dist: parsec==3.17
|
|
37
|
+
Requires-Dist: reportlab==5.0.0
|
|
38
|
+
Requires-Dist: scipy>=1.15
|
|
39
|
+
Requires-Dist: traits==7.1.0
|
|
40
|
+
Requires-Dist: traitsui==8.0.0
|
|
41
|
+
Requires-Dist: types-reportlab==4.5.1.20260712
|
|
39
42
|
Dynamic: license-file
|
|
40
43
|
|
|
41
44
|
[](https://results.pre-commit.ci/latest/github/capn-freako/PyAMI/master)
|
|
@@ -5,25 +5,27 @@ build-backend = "setuptools.build_meta"
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "pyibis-ami"
|
|
7
7
|
description = "Facilitates working directly with IBIS-AMI DLLs from the Python command prompt."
|
|
8
|
-
version = "9.
|
|
8
|
+
version = "9.3.0"
|
|
9
9
|
authors = [ {name = "David Banas", email = "capn.freako@gmail.com"}
|
|
10
10
|
]
|
|
11
11
|
readme = "README.md"
|
|
12
|
-
requires-python = ">=3.10,<3.
|
|
12
|
+
requires-python = ">=3.10,<3.14"
|
|
13
13
|
dependencies = [
|
|
14
|
-
"chaco
|
|
15
|
-
"click
|
|
16
|
-
"empy
|
|
17
|
-
"importlib_resources",
|
|
18
|
-
"jupyter",
|
|
19
|
-
"lxml-html-clean
|
|
14
|
+
"chaco==6.1.1",
|
|
15
|
+
"click==8.4.2",
|
|
16
|
+
"empy==4.2.1",
|
|
17
|
+
"importlib_resources==7.1.0",
|
|
18
|
+
"jupyter==1.1.1",
|
|
19
|
+
"lxml-html-clean==0.4.5",
|
|
20
20
|
"matplotlib>=3.6.1",
|
|
21
|
-
"numpy>=1.26
|
|
22
|
-
"papermill",
|
|
23
|
-
"parsec
|
|
24
|
-
"reportlab
|
|
25
|
-
"scipy>=1.
|
|
26
|
-
"
|
|
21
|
+
"numpy>=1.26",
|
|
22
|
+
"papermill==2.7.0",
|
|
23
|
+
"parsec==3.17",
|
|
24
|
+
"reportlab==5.0.0",
|
|
25
|
+
"scipy>=1.15",
|
|
26
|
+
"traits==7.1.0",
|
|
27
|
+
"traitsui==8.0.0",
|
|
28
|
+
"types-reportlab==4.5.1.20260712",
|
|
27
29
|
]
|
|
28
30
|
keywords=["ibis-ami"]
|
|
29
31
|
classifiers=[
|
|
@@ -36,6 +38,7 @@ classifiers=[
|
|
|
36
38
|
"Programming Language :: Python :: 3.10",
|
|
37
39
|
"Programming Language :: Python :: 3.11",
|
|
38
40
|
"Programming Language :: Python :: 3.12",
|
|
41
|
+
"Programming Language :: Python :: 3.13",
|
|
39
42
|
"Programming Language :: Python :: Implementation :: CPython",
|
|
40
43
|
"Topic :: System :: Emulators",
|
|
41
44
|
"Topic :: Utilities",
|
|
@@ -93,6 +96,13 @@ ignore = [
|
|
|
93
96
|
exclude = [
|
|
94
97
|
"*.ipynb"
|
|
95
98
|
]
|
|
99
|
+
lint.ignore = [
|
|
100
|
+
"UP045", # "Optional[X] -> "X | None"
|
|
101
|
+
"I001", # import formatting
|
|
102
|
+
"BLE001", # blind exceptions
|
|
103
|
+
"UP031", # format specifiers instead of '%'
|
|
104
|
+
"RUF022", # unsorted all
|
|
105
|
+
]
|
|
96
106
|
|
|
97
107
|
[[tool.mypy.overrides]]
|
|
98
108
|
module = [
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pyibis-ami
|
|
3
|
-
Version: 9.
|
|
3
|
+
Version: 9.3.0
|
|
4
4
|
Summary: Facilitates working directly with IBIS-AMI DLLs from the Python command prompt.
|
|
5
5
|
Author-email: David Banas <capn.freako@gmail.com>
|
|
6
6
|
Project-URL: Documentation, https://pyibis-ami.readthedocs.io/en/latest/
|
|
@@ -17,25 +17,28 @@ Classifier: Programming Language :: Python :: 3
|
|
|
17
17
|
Classifier: Programming Language :: Python :: 3.10
|
|
18
18
|
Classifier: Programming Language :: Python :: 3.11
|
|
19
19
|
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
21
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
21
22
|
Classifier: Topic :: System :: Emulators
|
|
22
23
|
Classifier: Topic :: Utilities
|
|
23
|
-
Requires-Python: <3.
|
|
24
|
+
Requires-Python: <3.14,>=3.10
|
|
24
25
|
Description-Content-Type: text/markdown
|
|
25
26
|
License-File: LICENSE
|
|
26
|
-
Requires-Dist: chaco
|
|
27
|
-
Requires-Dist: click
|
|
28
|
-
Requires-Dist: empy
|
|
29
|
-
Requires-Dist: importlib_resources
|
|
30
|
-
Requires-Dist: jupyter
|
|
31
|
-
Requires-Dist: lxml-html-clean
|
|
27
|
+
Requires-Dist: chaco==6.1.1
|
|
28
|
+
Requires-Dist: click==8.4.2
|
|
29
|
+
Requires-Dist: empy==4.2.1
|
|
30
|
+
Requires-Dist: importlib_resources==7.1.0
|
|
31
|
+
Requires-Dist: jupyter==1.1.1
|
|
32
|
+
Requires-Dist: lxml-html-clean==0.4.5
|
|
32
33
|
Requires-Dist: matplotlib>=3.6.1
|
|
33
|
-
Requires-Dist: numpy
|
|
34
|
-
Requires-Dist: papermill
|
|
35
|
-
Requires-Dist: parsec
|
|
36
|
-
Requires-Dist: reportlab
|
|
37
|
-
Requires-Dist: scipy>=1.
|
|
38
|
-
Requires-Dist:
|
|
34
|
+
Requires-Dist: numpy>=1.26
|
|
35
|
+
Requires-Dist: papermill==2.7.0
|
|
36
|
+
Requires-Dist: parsec==3.17
|
|
37
|
+
Requires-Dist: reportlab==5.0.0
|
|
38
|
+
Requires-Dist: scipy>=1.15
|
|
39
|
+
Requires-Dist: traits==7.1.0
|
|
40
|
+
Requires-Dist: traitsui==8.0.0
|
|
41
|
+
Requires-Dist: types-reportlab==4.5.1.20260712
|
|
39
42
|
Dynamic: license-file
|
|
40
43
|
|
|
41
44
|
[](https://results.pre-commit.ci/latest/github/capn-freako/PyAMI/master)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
chaco==6.1.1
|
|
2
|
+
click==8.4.2
|
|
3
|
+
empy==4.2.1
|
|
4
|
+
importlib_resources==7.1.0
|
|
5
|
+
jupyter==1.1.1
|
|
6
|
+
lxml-html-clean==0.4.5
|
|
7
|
+
matplotlib>=3.6.1
|
|
8
|
+
numpy>=1.26
|
|
9
|
+
papermill==2.7.0
|
|
10
|
+
parsec==3.17
|
|
11
|
+
reportlab==5.0.0
|
|
12
|
+
scipy>=1.15
|
|
13
|
+
traits==7.1.0
|
|
14
|
+
traitsui==8.0.0
|
|
15
|
+
types-reportlab==4.5.1.20260712
|
|
@@ -171,7 +171,7 @@ def mk_model(
|
|
|
171
171
|
"param_types": param_types,
|
|
172
172
|
"model_name": model_name,
|
|
173
173
|
"description": description,
|
|
174
|
-
"date": str(date.today()),
|
|
174
|
+
"date": str(date.today()), # noqa: DTZ011
|
|
175
175
|
},
|
|
176
176
|
)
|
|
177
177
|
try:
|
|
@@ -246,8 +246,8 @@ def mk_tests( # pylint: disable=too-many-locals
|
|
|
246
246
|
pname.mkdir(exist_ok=True)
|
|
247
247
|
pname = (pname / file_base_name).resolve()
|
|
248
248
|
pname.mkdir(exist_ok=True)
|
|
249
|
-
for fname in test_defs.
|
|
250
|
-
desc, ami_defs, sim_defs, ref_fstr =
|
|
249
|
+
for fname, value in test_defs.items():
|
|
250
|
+
desc, ami_defs, sim_defs, ref_fstr = value
|
|
251
251
|
ami_str, ami_dict = ami_defs
|
|
252
252
|
sim_str, sim_dict = sim_defs
|
|
253
253
|
with open((pname / fname).with_suffix(".run"), "w", encoding="utf-8") as f:
|
|
@@ -258,8 +258,7 @@ def mk_tests( # pylint: disable=too-many-locals
|
|
|
258
258
|
pdict.update(dict(sim_comb))
|
|
259
259
|
f.write(f"\n('{ami_str.format(pdict=pdict)}_{sim_str.format(pdict=pdict)}', \\\n")
|
|
260
260
|
f.write(f" ({{'root_name': '{file_base_name}', \\\n")
|
|
261
|
-
for k, v in ami_comb
|
|
262
|
-
f.write(f" '{k}': {v}, \\\n")
|
|
261
|
+
f.writelines(f" '{k}': {v}, \\\n" for k, v in ami_comb)
|
|
263
262
|
f.write(" }, \\\n")
|
|
264
263
|
if sim_comb:
|
|
265
264
|
head, *tail = sim_comb
|
|
@@ -355,11 +355,12 @@ class AMIModel: # pylint: disable=too-many-instance-attributes
|
|
|
355
355
|
self._info_params = init_object.info_params # pylint: disable=attribute-defined-outside-init
|
|
356
356
|
|
|
357
357
|
# Check GetWave() consistency if possible.
|
|
358
|
-
if init_object.info_params and init_object.info_params["GetWave_Exists"] and
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
)
|
|
358
|
+
if (init_object.info_params and init_object.info_params["GetWave_Exists"] and
|
|
359
|
+
init_object.info_params["GetWave_Exists"].pvalue and
|
|
360
|
+
not self._amiGetWave): # noqa: E129
|
|
361
|
+
raise RuntimeError(
|
|
362
|
+
"Reserved parameter `GetWave_Exists` is True, but I can't bind to `AMI_GetWave()`!"
|
|
363
|
+
)
|
|
363
364
|
|
|
364
365
|
# Construct the AMI parameters string.
|
|
365
366
|
def sexpr(pname: str, pval: Any) -> str:
|
|
@@ -387,7 +388,7 @@ class AMIModel: # pylint: disable=too-many-instance-attributes
|
|
|
387
388
|
self._root_name = root_name # pylint: disable=attribute-defined-outside-init
|
|
388
389
|
ami_params_in = f"({root_name} "
|
|
389
390
|
for item in list(init_object.ami_params.items()):
|
|
390
|
-
if
|
|
391
|
+
if item[0] != "root_name":
|
|
391
392
|
ami_params_in += sexpr(item[0], item[1])
|
|
392
393
|
ami_params_in += ")"
|
|
393
394
|
self._ami_params_in = ami_params_in.encode("utf-8") # pylint: disable=attribute-defined-outside-init
|
|
@@ -424,7 +425,7 @@ class AMIModel: # pylint: disable=too-many-instance-attributes
|
|
|
424
425
|
print(f"&ami_params_out = {byref(self._ami_params_out)}")
|
|
425
426
|
print(f"&ami_mem_handle = {byref(self._ami_mem_handle)}") # type: ignore
|
|
426
427
|
print(f"&msg = {byref(self._msg)}")
|
|
427
|
-
raise
|
|
428
|
+
raise
|
|
428
429
|
|
|
429
430
|
# Initialize attributes used by getWave().
|
|
430
431
|
bit_time = init_object.bit_time
|
|
@@ -441,7 +442,7 @@ class AMIModel: # pylint: disable=too-many-instance-attributes
|
|
|
441
442
|
)
|
|
442
443
|
self._getwave_step_response_out_params = None
|
|
443
444
|
|
|
444
|
-
def getWave(self, wave: Rvec, bits_per_call: int = 0) -> tuple[Rvec, Rvec, list[str]]:
|
|
445
|
+
def getWave(self, wave: Rvec, bits_per_call: int = 0) -> tuple[Rvec, Rvec, list[str]]:
|
|
445
446
|
"""
|
|
446
447
|
Performs time domain processing of input waveform, using the ``AMI_GetWave()`` function.
|
|
447
448
|
|
|
@@ -562,13 +563,12 @@ class AMIModel: # pylint: disable=too-many-instance-attributes
|
|
|
562
563
|
ts = self.sample_interval
|
|
563
564
|
info_params = self._info_params
|
|
564
565
|
ignore_bits = 0
|
|
565
|
-
if info_params:
|
|
566
|
-
|
|
567
|
-
ignore_bits = info_params["Ignore_Bits"].pvalue
|
|
566
|
+
if info_params and "Ignore_Bits" in info_params:
|
|
567
|
+
ignore_bits = info_params["Ignore_Bits"].pvalue
|
|
568
568
|
|
|
569
569
|
# Capture/convert instance variables.
|
|
570
|
-
chnl_imp = np.array(self.channel_response) * ts
|
|
571
|
-
out_imp = np.array(self.initOut) * ts
|
|
570
|
+
chnl_imp = np.array(self.channel_response[:self.row_size]) * ts # input (a.k.a. - "channel") impulse response (V/sample)
|
|
571
|
+
out_imp = np.array(self.initOut[:self.row_size]) * ts # output impulse response (V/sample)
|
|
572
572
|
|
|
573
573
|
# Calculate some needed intermediate values.
|
|
574
574
|
nspui = int(ui / ts) # samps per UI
|
|
@@ -584,7 +584,7 @@ class AMIModel: # pylint: disable=too-many-instance-attributes
|
|
|
584
584
|
):
|
|
585
585
|
pass
|
|
586
586
|
else:
|
|
587
|
-
h_model = deconv_same(out_imp, chnl_imp)
|
|
587
|
+
h_model = deconv_same(out_imp, chnl_imp)
|
|
588
588
|
rslt[IMP_RESP_INIT] = np.roll(h_model, -len(h_model) // 2 + 3 * nspui)
|
|
589
589
|
|
|
590
590
|
h_init = np.roll(out_imp, pad_samps)
|
|
@@ -7,6 +7,10 @@ Original date: December 24, 2016
|
|
|
7
7
|
Copyright (c) 2019 David Banas; all rights reserved World wide.
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
|
+
from typing import ClassVar
|
|
11
|
+
|
|
12
|
+
from traits.api import Bool, Float, HasTraits, List, Str
|
|
13
|
+
|
|
10
14
|
#####
|
|
11
15
|
# AMI parameter
|
|
12
16
|
#####
|
|
@@ -179,7 +183,7 @@ class AMIParameter: # pylint: disable=too-many-instance-attributes,too-few-publ
|
|
|
179
183
|
# The idea is to allow this class to grow along with the IBIS
|
|
180
184
|
# standard, without having to change any of its boilerplate.
|
|
181
185
|
|
|
182
|
-
_param_def_tag_procs = {
|
|
186
|
+
_param_def_tag_procs: ClassVar = {
|
|
183
187
|
"Usage": _set_usage,
|
|
184
188
|
"Type": _set_type,
|
|
185
189
|
"Format": _set_format,
|
|
@@ -326,3 +330,66 @@ class AMIParameter: # pylint: disable=too-many-instance-attributes,too-few-publ
|
|
|
326
330
|
|
|
327
331
|
def __str__(self):
|
|
328
332
|
return f"{self.pvalue}"
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
#####
|
|
336
|
+
# AMI parameter tuner
|
|
337
|
+
#####
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
# pylint: disable=too-many-instance-attributes,too-few-public-methods
|
|
341
|
+
class AmiParamTuner(HasTraits):
|
|
342
|
+
"""Object used to populate the rows of a Tx/Rx AMI parameter tuning table.
|
|
343
|
+
|
|
344
|
+
Represents one *Model_Specific* 'In'/'InOut' AMI parameter, of 'Range'
|
|
345
|
+
format, offered to the user for inclusion in EQ optimization.
|
|
346
|
+
"""
|
|
347
|
+
|
|
348
|
+
name = Str("(noname)") # Display name (fully hierarchical, e.g. "tx_preset_coeffs_main").
|
|
349
|
+
branch_names = List(Str) # Hierarchical path for `fetch_param_val()`/`set_param_val()`.
|
|
350
|
+
enabled = Bool(False) # Will participate in EQ optimization when *True*.
|
|
351
|
+
min_val = Float(0.0) # Minimum allowed value during optimization.
|
|
352
|
+
max_val = Float(0.0) # Maximum allowed value during optimization.
|
|
353
|
+
step = Float(0.0) # Increment used during optimization.
|
|
354
|
+
value = Float(0.0) # Current value.
|
|
355
|
+
is_int = Bool(False) # Round swept values to the nearest integer when *True*.
|
|
356
|
+
|
|
357
|
+
# pylint: disable=too-many-arguments,too-many-positional-arguments
|
|
358
|
+
def __init__(
|
|
359
|
+
self, name: str = "(noname)", branch_names: "list[str] | None" = None, enabled: bool = False,
|
|
360
|
+
min_val: float = 0.0, max_val: float = 0.0, step: float = 0.0, value: float = 0.0, is_int: bool = False
|
|
361
|
+
):
|
|
362
|
+
"""
|
|
363
|
+
Allows user to define properties, at instantiation.
|
|
364
|
+
|
|
365
|
+
Keyword Args:
|
|
366
|
+
name: Parameter name/label.
|
|
367
|
+
Default: "(noname)"
|
|
368
|
+
branch_names: Hierarchical path to the parameter, rooted at "Model_Specific".
|
|
369
|
+
Default: []
|
|
370
|
+
enabled: Will participate in EQ optimization when *True*.
|
|
371
|
+
Default: *False*
|
|
372
|
+
min_val: Minimum allowed value during optimization.
|
|
373
|
+
Default: 0.0
|
|
374
|
+
max_val: Maximum allowed value during optimization.
|
|
375
|
+
Default: 0.0
|
|
376
|
+
step: Increment used during optimization.
|
|
377
|
+
Default: 0.0
|
|
378
|
+
value: Current value.
|
|
379
|
+
Default: 0.0
|
|
380
|
+
is_int: Round swept values to the nearest integer when *True*.
|
|
381
|
+
Default: *False*
|
|
382
|
+
"""
|
|
383
|
+
|
|
384
|
+
# Super-class initialization is ABSOLUTELY NECESSARY, in order
|
|
385
|
+
# to get all the Traits/UI machinery setup correctly.
|
|
386
|
+
super().__init__()
|
|
387
|
+
|
|
388
|
+
self.name = name
|
|
389
|
+
self.branch_names = branch_names or []
|
|
390
|
+
self.enabled = enabled
|
|
391
|
+
self.min_val = min_val
|
|
392
|
+
self.max_val = max_val
|
|
393
|
+
self.step = step
|
|
394
|
+
self.value = value
|
|
395
|
+
self.is_int = is_int
|
|
@@ -11,7 +11,8 @@ from __future__ import annotations
|
|
|
11
11
|
from ctypes import c_double
|
|
12
12
|
from dataclasses import dataclass
|
|
13
13
|
import re
|
|
14
|
-
from typing import Any,
|
|
14
|
+
from typing import Any, NewType, Optional, TypeAlias
|
|
15
|
+
from collections.abc import Callable
|
|
15
16
|
|
|
16
17
|
import numpy as np
|
|
17
18
|
from numpy.typing import NDArray
|
|
@@ -21,7 +22,7 @@ from traitsui.api import Group, HGroup, Item, VGroup, View
|
|
|
21
22
|
from traitsui.menu import ModalButtons
|
|
22
23
|
|
|
23
24
|
from .model import AMIModelInitializer
|
|
24
|
-
from .parameter import AMIParamError, AMIParameter
|
|
25
|
+
from .parameter import AmiParamTuner, AMIParamError, AMIParameter
|
|
25
26
|
from .reserved_parameter_names import AmiReservedParameterName, RESERVED_PARAM_NAMES
|
|
26
27
|
|
|
27
28
|
# New types and aliases.
|
|
@@ -47,7 +48,7 @@ class AmiLeaf:
|
|
|
47
48
|
@dataclass(frozen=True)
|
|
48
49
|
class AmiBranch:
|
|
49
50
|
name: AmiName
|
|
50
|
-
children: list[
|
|
51
|
+
children: list[AmiNode]
|
|
51
52
|
|
|
52
53
|
|
|
53
54
|
AmiNode: TypeAlias = AmiLeaf | AmiBranch
|
|
@@ -60,7 +61,7 @@ AmiRootName = NewType("AmiRootName", str)
|
|
|
60
61
|
ReservedParamDict: TypeAlias = dict[AmiReservedParameterName, AMIParameter]
|
|
61
62
|
ModelSpecificDict: TypeAlias = dict[ParamName, "'AMIParameter' | 'ModelSpecificDict'"]
|
|
62
63
|
|
|
63
|
-
__all__ = [
|
|
64
|
+
__all__ = [ # ruff: ignore=RUF022
|
|
64
65
|
"ParamName", "ParamValue", "Parameters", "ParamValues",
|
|
65
66
|
"AmiName", "AmiAtom", "AmiNode", "AmiNodeParser", "AmiParser",
|
|
66
67
|
"ami_parse", "AMIParamConfigurator"]
|
|
@@ -186,32 +187,125 @@ class AMIParamConfigurator(HasTraits):
|
|
|
186
187
|
return _param.pvalue
|
|
187
188
|
return None
|
|
188
189
|
|
|
189
|
-
def set_param_val(
|
|
190
|
-
|
|
190
|
+
def set_param_val(
|
|
191
|
+
self: AMIParamConfigurator,
|
|
192
|
+
branch_names: list[str],
|
|
193
|
+
new_val: Any
|
|
194
|
+
) -> None:
|
|
195
|
+
"""
|
|
196
|
+
Sets the value of the parameter found by traversing 'branch_names'
|
|
191
197
|
or raises an exception if not found.
|
|
192
198
|
|
|
193
|
-
|
|
194
|
-
|
|
199
|
+
Args:
|
|
200
|
+
branch_names: A sequence of node names, used to traverse the parameter tree.
|
|
201
|
+
new_val: The value to assign to the target parameter.
|
|
202
|
+
|
|
203
|
+
Notes:
|
|
204
|
+
1. ``branch_names`` should *not* begin with <root_name>.
|
|
205
|
+
2. Be careful! There is no checking done here!
|
|
195
206
|
"""
|
|
196
207
|
|
|
197
208
|
param_dict = self.ami_param_defs
|
|
209
|
+
tname_parts: list[str] = [] # Fully hierarchical trait name, minus the root key.
|
|
210
|
+
root_key: Optional[str] = None
|
|
198
211
|
while branch_names:
|
|
199
212
|
branch_name = branch_names.pop(0)
|
|
213
|
+
if root_key is None:
|
|
214
|
+
root_key = branch_name # "Reserved_Parameters" or "Model_Specific"; not part of the trait name.
|
|
215
|
+
else:
|
|
216
|
+
tname_parts.append(branch_name)
|
|
200
217
|
if branch_name in param_dict:
|
|
201
|
-
param_dict = param_dict[branch_name]
|
|
218
|
+
param_dict = param_dict[branch_name] # type: ignore
|
|
202
219
|
else:
|
|
203
220
|
raise ValueError(
|
|
204
221
|
f"Failed parameter tree search looking for: {branch_name}; available keys: {param_dict.keys()}"
|
|
205
222
|
)
|
|
206
223
|
if isinstance(param_dict, AMIParameter):
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
224
|
+
# Coerce to the parameter's declared type. Notably: a `Bool` trait rejects a
|
|
225
|
+
# float outright, and a vendor's AMI_Init() parser may choke on "1.0" where an
|
|
226
|
+
# Integer-typed value (e.g. a List-format mode selector) is expected.
|
|
227
|
+
if param_dict.ptype == "Boolean":
|
|
228
|
+
if not isinstance(new_val, bool):
|
|
229
|
+
if isinstance(new_val, str):
|
|
230
|
+
match new_val:
|
|
231
|
+
case "FALSE" | "False" | "false":
|
|
232
|
+
new_val = False
|
|
233
|
+
case _:
|
|
234
|
+
new_val = bool(new_val)
|
|
235
|
+
else:
|
|
236
|
+
new_val = bool(new_val)
|
|
237
|
+
elif param_dict.ptype in ("Integer", "Tap"):
|
|
238
|
+
new_val = int(new_val)
|
|
239
|
+
# `pvalue`, for 'List' format, holds the list of *legal* values, not the
|
|
240
|
+
# current one (that lives on the Trait) -- leave it alone, to avoid corrupting it.
|
|
241
|
+
if param_dict.pformat != "List":
|
|
242
|
+
param_dict.pvalue = new_val
|
|
243
|
+
tname = "_".join(tname_parts)
|
|
244
|
+
if tname:
|
|
245
|
+
try:
|
|
246
|
+
self.trait_set(**{f"{tname}_": new_val})
|
|
247
|
+
except Exception: # pylint: disable=broad-exception-caught
|
|
248
|
+
self.trait_set(**{tname: new_val})
|
|
212
249
|
else:
|
|
213
250
|
raise TypeError(f"{param_dict} is not of type: AMIParameter!")
|
|
214
251
|
|
|
252
|
+
@property
|
|
253
|
+
def tunable_params(self) -> list[tuple[list[str], AMIParameter]]:
|
|
254
|
+
"""Every *Model Specific* parameter of type 'In' or 'InOut' that is a
|
|
255
|
+
candidate for automated sweeping/optimization, paired with its fully
|
|
256
|
+
hierarchical branch name path. This includes:
|
|
257
|
+
|
|
258
|
+
- numeric 'Range'-format parameters (real min/max bounds),
|
|
259
|
+
- 'Boolean' parameters (True/False), and
|
|
260
|
+
- 'List'-format 'Integer' parameters whose legal values form a
|
|
261
|
+
contiguous range (e.g. a mode selector like `(List 0 1)`) --
|
|
262
|
+
this is deliberately conservative: a non-contiguous legal set
|
|
263
|
+
(e.g. `(List 6 12 18)`) can't be swept with a uniform step
|
|
264
|
+
without risking illegal intermediate values, so it's excluded.
|
|
265
|
+
|
|
266
|
+
The returned branch name paths are ready to pass directly into
|
|
267
|
+
``fetch_param_val()``/``set_param_val()`` (i.e. - they are rooted at
|
|
268
|
+
``"Model_Specific"``).
|
|
269
|
+
"""
|
|
270
|
+
return _walk_tunable_params(self._model_specific_dict, ["Model_Specific"])
|
|
271
|
+
|
|
272
|
+
def mk_tap_tuners(self) -> list[AmiParamTuner]:
|
|
273
|
+
"""Build a fresh list of `AmiParamTuner`s from this AMI model's tunable
|
|
274
|
+
Model_Specific parameters (see `tunable_params` for exactly which ones
|
|
275
|
+
qualify: Range-format, Boolean, and contiguous Integer/List "mode
|
|
276
|
+
selector" parameters)."""
|
|
277
|
+
tuners = []
|
|
278
|
+
for branch_names, param in self.tunable_params:
|
|
279
|
+
tname = "_".join(branch_names[1:]) # Drop the "Model_Specific" root.
|
|
280
|
+
if param.pformat == "Range":
|
|
281
|
+
pmin, pmax = float(param.pmin), float(param.pmax)
|
|
282
|
+
step = (pmax - pmin) / 10 or 1.0
|
|
283
|
+
is_int = param.ptype == "Integer"
|
|
284
|
+
value = float(param.pvalue)
|
|
285
|
+
elif param.pformat == "Value" and param.ptype == "Boolean":
|
|
286
|
+
pmin, pmax, step, is_int = 0.0, 1.0, 1.0, True
|
|
287
|
+
value = float(param.pvalue)
|
|
288
|
+
else: # List-format Integer "mode selector" (contiguous legal values).
|
|
289
|
+
vals = [float(v) for v in param.pvalue]
|
|
290
|
+
pmin, pmax, step, is_int = min(vals), max(vals), 1.0, True
|
|
291
|
+
# `pvalue`, for 'List' format, is the list of *legal* values, not the
|
|
292
|
+
# current one -- that lives on the Trait `make_gui_items()` registered.
|
|
293
|
+
try:
|
|
294
|
+
value = float(self.trait_get(tname + "_")[tname + "_"])
|
|
295
|
+
except Exception: # pylint: disable=broad-exception-caught
|
|
296
|
+
value = float(self.trait_get(tname)[tname])
|
|
297
|
+
tuners.append(AmiParamTuner(
|
|
298
|
+
name=tname,
|
|
299
|
+
branch_names=branch_names,
|
|
300
|
+
enabled=False,
|
|
301
|
+
min_val=pmin,
|
|
302
|
+
max_val=pmax,
|
|
303
|
+
step=step,
|
|
304
|
+
value=value,
|
|
305
|
+
is_int=is_int,
|
|
306
|
+
))
|
|
307
|
+
return tuners
|
|
308
|
+
|
|
215
309
|
@property
|
|
216
310
|
def ami_parsing_errors(self) -> list[str]:
|
|
217
311
|
"""Any errors or warnings encountered, while parsing the AMI parameter
|
|
@@ -286,7 +380,7 @@ class AMIParamConfigurator(HasTraits):
|
|
|
286
380
|
elif isinstance(param, dict): # We received a dictionary of subparameters, in 'param'.
|
|
287
381
|
subs: ParamValues = {}
|
|
288
382
|
for sname in param:
|
|
289
|
-
subs.update(self.input_ami_param(param, sname, prefix=
|
|
383
|
+
subs.update(self.input_ami_param(param, sname, prefix=tname + "_")) # type: ignore
|
|
290
384
|
res[pname] = subs
|
|
291
385
|
return res
|
|
292
386
|
|
|
@@ -332,6 +426,47 @@ class AMIParamConfigurator(HasTraits):
|
|
|
332
426
|
return initializer
|
|
333
427
|
|
|
334
428
|
|
|
429
|
+
def _is_sweepable(param: AMIParameter) -> bool:
|
|
430
|
+
"See `AMIParamConfigurator.tunable_params` for the exact criteria."
|
|
431
|
+
if param.pusage not in ("In", "InOut"):
|
|
432
|
+
return False
|
|
433
|
+
if param.pformat == "Range":
|
|
434
|
+
return True
|
|
435
|
+
if param.pformat == "Value" and param.ptype == "Boolean":
|
|
436
|
+
return True
|
|
437
|
+
if param.pformat == "List" and param.ptype == "Integer":
|
|
438
|
+
vals = sorted(int(v) for v in param.pvalue)
|
|
439
|
+
return vals == list(range(vals[0], vals[-1] + 1)) # Contiguous?
|
|
440
|
+
return False
|
|
441
|
+
|
|
442
|
+
|
|
443
|
+
def _walk_tunable_params(
|
|
444
|
+
params: Parameters, prefix: list[str]
|
|
445
|
+
) -> list[tuple[list[str], AMIParameter]]:
|
|
446
|
+
"""Recursively collect every sweepable (see `_is_sweepable`) leaf of a
|
|
447
|
+
*Model Specific* parameter (sub)tree.
|
|
448
|
+
|
|
449
|
+
Args:
|
|
450
|
+
params: The (sub)dictionary of AMI parameters to walk.
|
|
451
|
+
prefix: The branch name path leading to ``params``.
|
|
452
|
+
|
|
453
|
+
Returns:
|
|
454
|
+
A list of (branch name path, parameter) pairs, one per sweepable leaf.
|
|
455
|
+
"""
|
|
456
|
+
|
|
457
|
+
results: list[tuple[list[str], AMIParameter]] = []
|
|
458
|
+
for pname, param in params.items():
|
|
459
|
+
if pname == "description": # A branch's optional descriptive text, not a subparameter.
|
|
460
|
+
continue
|
|
461
|
+
path = prefix + [pname]
|
|
462
|
+
if isinstance(param, AMIParameter):
|
|
463
|
+
if _is_sweepable(param):
|
|
464
|
+
results.append((path, param))
|
|
465
|
+
else: # Subparameter branch.
|
|
466
|
+
results.extend(_walk_tunable_params(param, path))
|
|
467
|
+
return results
|
|
468
|
+
|
|
469
|
+
|
|
335
470
|
#####
|
|
336
471
|
# AMI file parser.
|
|
337
472
|
#####
|
|
@@ -367,7 +502,8 @@ integ = lexeme(regex(r"[-+]?[0-9]+"))
|
|
|
367
502
|
nat = lexeme(regex(r"[0-9]+"))
|
|
368
503
|
# tap_ix = (integ << whitespace).parsecmap(int2tap) # Doesn't work!
|
|
369
504
|
# tap_ix = integ.parsecmap(int2tap) # Breaks parsing of node names that begin with a numeral!
|
|
370
|
-
tap_ix = integ.parsecmap(int2tap) << whitespace
|
|
505
|
+
# tap_ix = integ.parsecmap(int2tap) << whitespace # This fails; I don't know why.
|
|
506
|
+
tap_ix = lexeme(integ.parsecmap(int2tap)) # This works, as an alternative to the line above.
|
|
371
507
|
symbol = lexeme(regex(r"[0-9a-zA-Z_][^\s()]*"))
|
|
372
508
|
true = lexeme(string("True")).result(True)
|
|
373
509
|
false = lexeme(string("False")).result(False)
|
|
@@ -557,7 +693,7 @@ def parse_ami_file_contents( # pylint: disable=too-many-locals,too-many-branche
|
|
|
557
693
|
init_returns_impulse_found = False
|
|
558
694
|
getwave_exists_found = False
|
|
559
695
|
model_spec_found = False
|
|
560
|
-
root_name, params =
|
|
696
|
+
root_name, params = next(iter(param_dict.items()))
|
|
561
697
|
description = ""
|
|
562
698
|
reserved_params_dict: dict[AmiReservedParameterName, AMIParameter] = {}
|
|
563
699
|
model_specific_dict: ModelSpecificDict = {}
|
|
@@ -605,8 +741,7 @@ def parse_ami_file_contents( # pylint: disable=too-many-locals,too-many-branche
|
|
|
605
741
|
# Legacy client code support:
|
|
606
742
|
def parse_ami_param_defs(file_contents: str) -> tuple[ParseErrMsg, dict[str, Any]]:
|
|
607
743
|
"The legacy version of ``parse_ami_file_contents()``."
|
|
608
|
-
|
|
609
|
-
errors, warnings, root_name, description, reserved_params_dict, model_specific_dict = parse_ami_file_contents(file_contents)
|
|
744
|
+
errors, _warnings, root_name, description, reserved_params_dict, model_specific_dict = parse_ami_file_contents(file_contents)
|
|
610
745
|
if errors:
|
|
611
746
|
err_msg = errors[0]
|
|
612
747
|
else:
|
|
@@ -649,8 +784,8 @@ def make_gui(params: ModelSpecificDict) -> tuple[Group, list[tuple[str, TraitTyp
|
|
|
649
784
|
# but no pre-existing enclosing group, in a ``VGroup``.
|
|
650
785
|
# - Generate grouped parameter names and list of unique group names.
|
|
651
786
|
pnames = list(params.keys())
|
|
652
|
-
pnames_split =
|
|
653
|
-
group_names =
|
|
787
|
+
pnames_split = [n.split('_') for n in pnames]
|
|
788
|
+
group_names = [xs[0] for xs in pnames_split]
|
|
654
789
|
grouped_pnames = list(zip(pnames, group_names))
|
|
655
790
|
# The following doesn't preserve the parameter ordering of the AMI file!
|
|
656
791
|
# for group_name in set(group_names): # Iterate over unique group names.
|
|
@@ -728,7 +863,7 @@ def make_gui_items( # pylint: disable=too-many-locals,too-many-branches
|
|
|
728
863
|
if list_tips:
|
|
729
864
|
tmp_dict: dict[str, Any] = {}
|
|
730
865
|
tmp_dict.update(list(zip(list_tips, param.pvalue)))
|
|
731
|
-
val =
|
|
866
|
+
val = next(iter(tmp_dict.keys()))
|
|
732
867
|
if default:
|
|
733
868
|
for tip in tmp_dict.items():
|
|
734
869
|
if tip[1] == default:
|