pyibis-ami 7.1.0__tar.gz → 7.2.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.
Files changed (52) hide show
  1. {pyibis_ami-7.1.0/src/PyIBIS_AMI.egg-info → pyibis_ami-7.2.0}/PKG-INFO +1 -1
  2. {pyibis_ami-7.1.0 → pyibis_ami-7.2.0}/docs/source/conf.py +4 -2
  3. {pyibis_ami-7.1.0 → pyibis_ami-7.2.0}/pyproject.toml +1 -1
  4. {pyibis_ami-7.1.0 → pyibis_ami-7.2.0/src/PyIBIS_AMI.egg-info}/PKG-INFO +1 -1
  5. {pyibis_ami-7.1.0 → pyibis_ami-7.2.0}/src/PyIBIS_AMI.egg-info/SOURCES.txt +1 -0
  6. {pyibis_ami-7.1.0 → pyibis_ami-7.2.0}/src/pyibisami/ami/model.py +1 -1
  7. {pyibis_ami-7.1.0 → pyibis_ami-7.2.0}/src/pyibisami/ami/parameter.py +0 -59
  8. {pyibis_ami-7.1.0 → pyibis_ami-7.2.0}/src/pyibisami/ami/parser.py +123 -84
  9. pyibis_ami-7.2.0/src/pyibisami/ami/reserved_parameter_names.py +84 -0
  10. {pyibis_ami-7.1.0 → pyibis_ami-7.2.0}/src/pyibisami/common.py +4 -2
  11. {pyibis_ami-7.1.0 → pyibis_ami-7.2.0}/LICENSE +0 -0
  12. {pyibis_ami-7.1.0 → pyibis_ami-7.2.0}/MANIFEST.in +0 -0
  13. {pyibis_ami-7.1.0 → pyibis_ami-7.2.0}/README.md +0 -0
  14. {pyibis_ami-7.1.0 → pyibis_ami-7.2.0}/docs/source/ami.rst +0 -0
  15. {pyibis_ami-7.1.0 → pyibis_ami-7.2.0}/docs/source/ibis.rst +0 -0
  16. {pyibis_ami-7.1.0 → pyibis_ami-7.2.0}/docs/source/index.rst +0 -0
  17. {pyibis_ami-7.1.0 → pyibis_ami-7.2.0}/docs/source/install.rst +0 -0
  18. {pyibis_ami-7.1.0 → pyibis_ami-7.2.0}/docs/source/modules.rst +0 -0
  19. {pyibis_ami-7.1.0 → pyibis_ami-7.2.0}/docs/source/tools.rst +0 -0
  20. {pyibis_ami-7.1.0 → pyibis_ami-7.2.0}/examples/all_taps.run +0 -0
  21. {pyibis_ami-7.1.0 → pyibis_ami-7.2.0}/examples/bandwidth.run +0 -0
  22. {pyibis_ami-7.1.0 → pyibis_ami-7.2.0}/examples/dummy.em +0 -0
  23. {pyibis_ami-7.1.0 → pyibis_ami-7.2.0}/examples/examples.md +0 -0
  24. {pyibis_ami-7.1.0 → pyibis_ami-7.2.0}/examples/freq_resp.em +0 -0
  25. {pyibis_ami-7.1.0 → pyibis_ami-7.2.0}/examples/impulse.em +0 -0
  26. {pyibis_ami-7.1.0 → pyibis_ami-7.2.0}/examples/impulse_response_8ma.txt +0 -0
  27. {pyibis_ami-7.1.0 → pyibis_ami-7.2.0}/examples/mode.run +0 -0
  28. {pyibis_ami-7.1.0 → pyibis_ami-7.2.0}/examples/stratix4_ami_tx_runs/all_taps.run +0 -0
  29. {pyibis_ami-7.1.0 → pyibis_ami-7.2.0}/examples/stratix4_ami_tx_runs/test_results.xml +0 -0
  30. {pyibis_ami-7.1.0 → pyibis_ami-7.2.0}/examples/test_results/test_results.xsl +0 -0
  31. {pyibis_ami-7.1.0 → pyibis_ami-7.2.0}/setup.cfg +0 -0
  32. {pyibis_ami-7.1.0 → pyibis_ami-7.2.0}/src/PyIBIS_AMI.egg-info/dependency_links.txt +0 -0
  33. {pyibis_ami-7.1.0 → pyibis_ami-7.2.0}/src/PyIBIS_AMI.egg-info/entry_points.txt +0 -0
  34. {pyibis_ami-7.1.0 → pyibis_ami-7.2.0}/src/PyIBIS_AMI.egg-info/requires.txt +0 -0
  35. {pyibis_ami-7.1.0 → pyibis_ami-7.2.0}/src/PyIBIS_AMI.egg-info/top_level.txt +0 -0
  36. {pyibis_ami-7.1.0 → pyibis_ami-7.2.0}/src/pyibisami/IBIS_AMI_Checker.ipynb +0 -0
  37. {pyibis_ami-7.1.0 → pyibis_ami-7.2.0}/src/pyibisami/IBIS_AMI_Tester.ipynb +0 -0
  38. {pyibis_ami-7.1.0 → pyibis_ami-7.2.0}/src/pyibisami/__init__.py +0 -0
  39. {pyibis_ami-7.1.0 → pyibis_ami-7.2.0}/src/pyibisami/__main__.py +0 -0
  40. {pyibis_ami-7.1.0 → pyibis_ami-7.2.0}/src/pyibisami/ami/__init__.py +0 -0
  41. {pyibis_ami-7.1.0 → pyibis_ami-7.2.0}/src/pyibisami/ami/config.py +0 -0
  42. {pyibis_ami-7.1.0 → pyibis_ami-7.2.0}/src/pyibisami/ami/generic.ami.em +0 -0
  43. {pyibis_ami-7.1.0 → pyibis_ami-7.2.0}/src/pyibisami/ami/generic.ibs.em +0 -0
  44. {pyibis_ami-7.1.0 → pyibis_ami-7.2.0}/src/pyibisami/ibis/__init__.py +0 -0
  45. {pyibis_ami-7.1.0 → pyibis_ami-7.2.0}/src/pyibisami/ibis/file.py +0 -0
  46. {pyibis_ami-7.1.0 → pyibis_ami-7.2.0}/src/pyibisami/ibis/model.py +0 -0
  47. {pyibis_ami-7.1.0 → pyibis_ami-7.2.0}/src/pyibisami/ibis/parser.py +0 -0
  48. {pyibis_ami-7.1.0 → pyibis_ami-7.2.0}/src/pyibisami/tools/__init__.py +0 -0
  49. {pyibis_ami-7.1.0 → pyibis_ami-7.2.0}/src/pyibisami/tools/run_notebook.py +0 -0
  50. {pyibis_ami-7.1.0 → pyibis_ami-7.2.0}/src/pyibisami/tools/run_tests.py +0 -0
  51. {pyibis_ami-7.1.0 → pyibis_ami-7.2.0}/src/pyibisami/tools/test_results.xsl +0 -0
  52. {pyibis_ami-7.1.0 → pyibis_ami-7.2.0}/tests/test_run_tests.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: pyibis-ami
3
- Version: 7.1.0
3
+ Version: 7.2.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
  License: BSD
@@ -84,9 +84,11 @@ author = "David Banas"
84
84
  # built documents.
85
85
  #
86
86
  # The short X.Y version.
87
- version = "7.1"
87
+ _ver = os.environ['PROJ_VER']
88
+ # The short X.Y version.
89
+ version = '.'.join(_ver.split('.')[0:2])
88
90
  # The full version, including alpha/beta/rc tags.
89
- release = "7.1.0"
91
+ release = _ver
90
92
 
91
93
  # The language for content autogenerated by Sphinx. Refer to documentation
92
94
  # for a list of supported languages.
@@ -5,7 +5,7 @@ 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 = "7.1.0"
8
+ version = "7.2.0"
9
9
  authors = [ {name = "David Banas", email = "capn.freako@gmail.com"}
10
10
  ]
11
11
  readme = "README.md"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: pyibis-ami
3
- Version: 7.1.0
3
+ Version: 7.2.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
  License: BSD
@@ -44,6 +44,7 @@ src/pyibisami/ami/generic.ibs.em
44
44
  src/pyibisami/ami/model.py
45
45
  src/pyibisami/ami/parameter.py
46
46
  src/pyibisami/ami/parser.py
47
+ src/pyibisami/ami/reserved_parameter_names.py
47
48
  src/pyibisami/ibis/__init__.py
48
49
  src/pyibisami/ibis/file.py
49
50
  src/pyibisami/ibis/model.py
@@ -481,7 +481,7 @@ class AMIModel: # pylint: disable=too-many-instance-attributes
481
481
  ui = self.bit_time
482
482
  ts = self.sample_interval
483
483
  info_params = self.info_params
484
- ignore_bits = info_params["Ignore_Bits"] if "Ignore_Bits" in info_params else 0
484
+ ignore_bits = info_params["Ignore_Bits"].pvalue if "Ignore_Bits" in info_params else 0
485
485
 
486
486
  # Capture/convert instance variables.
487
487
  chnl_imp = np.array(self.channel_response) * ts # input (a.k.a. - "channel") impulse response (V/sample)
@@ -23,65 +23,6 @@ class AMIParameter: # pylint: disable=too-many-instance-attributes,too-few-publ
23
23
  parameter.
24
24
  """
25
25
 
26
- RESERVED_PARAM_NAMES = [
27
- "AMI_Version",
28
- "Init_Returns_Impulse",
29
- "GetWave_Exists",
30
- "Use_Init_Output",
31
- "Max_Init_Aggressors",
32
- "Ignore_Bits",
33
- "Resolve_Exists",
34
- "Model_Name",
35
- "Special_Param_Names",
36
- "Component_Name",
37
- "Signal_Name",
38
- "Rx_Decision_Time",
39
- "DC_Offset",
40
- "Rx_Use_Clock_Input",
41
- "Supporting_Files",
42
- "DLL_Path",
43
- "DLL_ID",
44
- "Tx_Jitter",
45
- "Tx_DCD",
46
- "Tx_Rj",
47
- "Tx_Dj",
48
- "Tx_Sj",
49
- "Tx_Sj_Frequency",
50
- "Rx_DCD",
51
- "Rx_Rj",
52
- "Rx_Dj",
53
- "Rx_Sj",
54
- "Rx_Clock_PDF",
55
- "Rx_Clock_Recovery_Mean",
56
- "Rx_Clock_Recovery_Rj",
57
- "Rx_Clock_Recovery_Dj",
58
- "Rx_Clock_Recovery_Sj",
59
- "Rx_Clock_Recovery_DCD",
60
- "Rx_Receiver_Sensitivity",
61
- "Rx_Noise",
62
- "Rx_GaussianNoise",
63
- "Rx_UniformNoise",
64
- "Modulation",
65
- "PAM4_Mapping",
66
- "PAM4_UpperThreshold",
67
- "PAM4_CenterThreshold",
68
- "PAM4_LowerThreshold",
69
- "PAM4_UpperEyeOffset",
70
- "PAM4_CenterEyeOffset",
71
- "PAM4_LowerEyeOffset",
72
- "Repeater_Type",
73
- "BCI_Protocol",
74
- "BCI_ID",
75
- "BCI_State",
76
- "BCI_Message_Interval_UI",
77
- "BCI_Training_UI",
78
- "BCI_Training_Mode",
79
- "Ts4file",
80
- "Tx_V",
81
- "Tx_R",
82
- "Rx_R",
83
- ]
84
-
85
26
  # Properties.
86
27
 
87
28
  # Note: They are read-only, despite the presence of apparent setters.
@@ -9,16 +9,18 @@ Copyright (c) 2019 David Banas; all rights reserved World wide.
9
9
 
10
10
  from ctypes import c_double
11
11
  import re
12
- from typing import Any, NewType, Optional, TypeAlias
12
+ from typing import Any, Callable, NewType, Optional, TypeAlias
13
13
 
14
+ import numpy as np
14
15
  from numpy.typing import NDArray
15
16
  from parsec import ParseError, generate, many, regex, string
16
17
  from traits.api import Bool, Enum, HasTraits, Range, Trait, TraitType
17
18
  from traitsui.api import Group, HGroup, Item, VGroup, View
18
19
  from traitsui.menu import ModalButtons
19
20
 
20
- from pyibisami.ami.model import AMIModelInitializer
21
- from pyibisami.ami.parameter import AMIParamError, AMIParameter
21
+ from .model import AMIModelInitializer
22
+ from .parameter import AMIParamError, AMIParameter
23
+ from .reserved_parameter_names import AmiReservedParameterName, RESERVED_PARAM_NAMES
22
24
 
23
25
  # New types and aliases.
24
26
  # Parameters = NewType('Parameters', dict[str, AMIParameter] | dict[str, 'Parameters'])
@@ -26,8 +28,25 @@ from pyibisami.ami.parameter import AMIParamError, AMIParameter
26
28
  # See: https://stackoverflow.com/questions/70894567/using-mypy-newtype-with-type-aliases-or-protocols
27
29
  ParamName = NewType("ParamName", str)
28
30
  ParamValue: TypeAlias = int | float | str | list["ParamValue"]
29
- Parameters: TypeAlias = dict[ParamName, AMIParameter] | dict[ParamName, 'Parameters']
30
- ParamValues: TypeAlias = dict[ParamName, ParamValue] | dict[ParamName, 'ParamValues']
31
+ Parameters: TypeAlias = dict[ParamName, "AMIParameter | 'Parameters'"]
32
+ ParamValues: TypeAlias = dict[ParamName, "ParamValue | 'ParamValues'"]
33
+
34
+ AmiName = NewType("AmiName", str)
35
+ AmiAtom: TypeAlias = bool | int | float | str
36
+ AmiExpr: TypeAlias = "AmiAtom | 'AmiNode'"
37
+ AmiNode: TypeAlias = tuple[AmiName, list[AmiExpr]]
38
+ AmiNodeParser: TypeAlias = Callable[[str], AmiNode]
39
+ AmiParser: TypeAlias = Callable[[str], tuple[AmiName, list[AmiNode]]] # Atoms may not exist at the root level.
40
+
41
+ ParseErrMsg = NewType("ParseErrMsg", str)
42
+ AmiRootName = NewType("AmiRootName", str)
43
+ ReservedParamDict: TypeAlias = dict[AmiReservedParameterName, AMIParameter]
44
+ ModelSpecificDict: TypeAlias = dict[ParamName, "AMIParameter | 'ModelSpecificDict'"]
45
+
46
+ __all__ = [
47
+ "ParamName", "ParamValue", "Parameters", "ParamValues",
48
+ "AmiName", "AmiAtom", "AmiExpr", "AmiNode", "AmiNodeParser", "AmiParser",
49
+ "ami_parse", "AMIParamConfigurator"]
31
50
 
32
51
  #####
33
52
  # AMI parameter configurator.
@@ -77,25 +96,19 @@ class AMIParamConfigurator(HasTraits):
77
96
  # to get all the Traits/UI machinery setup correctly.
78
97
  super().__init__()
79
98
 
80
- # Parse the AMI file contents, storing any errors or warnings,
81
- # and customize the view accordingly.
82
- err_str, param_dict = parse_ami_param_defs(ami_file_contents_str)
83
- if not param_dict:
84
- print("Empty dictionary returned by parse_ami_param_defs()!")
85
- print(f"Error message:\n{err_str}")
86
- raise KeyError("Failed to parse AMI file; see console for more detail.")
87
- top_branch = list(param_dict.items())[0]
88
- root_name = top_branch[0]
89
- param_dict = top_branch[1]
90
- if "Reserved_Parameters" not in param_dict:
91
- print(f"Error: {err_str}\nParameters: {param_dict}")
92
- raise KeyError("Unable to get 'Reserved_Parameters' from the parameter set.")
93
- if "Model_Specific" not in param_dict:
94
- print(f"Error: {err_str}\nParameters: {param_dict}")
95
- raise KeyError("Unable to get 'Model_Specific' from the parameter set.")
96
- pdict = param_dict["Reserved_Parameters"].copy()
97
- pdict.update(param_dict["Model_Specific"])
98
- gui_items, new_traits = make_gui(pdict)
99
+ # Parse the AMI file contents, storing any errors or warnings, and customize the view accordingly.
100
+ err_str, root_name, description, reserved_param_dict, model_specific_dict = parse_ami_file_contents(ami_file_contents_str)
101
+ assert reserved_param_dict, ValueError(
102
+ "\n".join([
103
+ "No 'Reserved_Parameters' section found!",
104
+ err_str
105
+ ]))
106
+ assert model_specific_dict, ValueError(
107
+ "\n".join([
108
+ "No 'Model_Specific' section found!",
109
+ err_str
110
+ ]))
111
+ gui_items, new_traits = make_gui(model_specific_dict)
99
112
  trait_names = []
100
113
  for trait in new_traits:
101
114
  self.add_trait(trait[0], trait[1])
@@ -104,8 +117,9 @@ class AMIParamConfigurator(HasTraits):
104
117
  self._root_name = root_name
105
118
  self._ami_parsing_errors = err_str
106
119
  self._content = gui_items
107
- self._param_dict = param_dict
108
- self._info_dict = {name: p.pvalue for (name, p) in list(param_dict["Reserved_Parameters"].items())}
120
+ self._reserved_param_dict = reserved_param_dict
121
+ self._model_specific_dict = model_specific_dict
122
+ self._description = description
109
123
 
110
124
  def __call__(self):
111
125
  self.open_gui()
@@ -187,12 +201,13 @@ class AMIParamConfigurator(HasTraits):
187
201
  return self._ami_parsing_errors
188
202
 
189
203
  @property
190
- def ami_param_defs(self) -> dict[ParamName, Any]:
204
+ def ami_param_defs(self) -> dict[str, ReservedParamDict | ModelSpecificDict]:
191
205
  """The entire AMI parameter definition dictionary.
192
206
 
193
207
  Should *not* be passed to ``AMIModelInitializer`` constructor!
194
208
  """
195
- return self._param_dict
209
+ return {"Reserved_Parameters": self._reserved_param_dict,
210
+ "Model_Specific": self._model_specific_dict}
196
211
 
197
212
  @property
198
213
  def input_ami_params(self) -> ParamValues:
@@ -203,9 +218,9 @@ class AMIParamConfigurator(HasTraits):
203
218
  Should be passed to ``AMIModelInitializer`` constructor.
204
219
  """
205
220
 
206
- res = {}
207
- res[ParamName("root_name")] = self._root_name
208
- params = self.ami_param_defs[ParamName("Model_Specific")]
221
+ res: ParamValues = {}
222
+ res[ParamName("root_name")] = str(self._root_name)
223
+ params = self._model_specific_dict
209
224
  for pname in params:
210
225
  res.update(self.input_ami_param(params, pname))
211
226
  return res
@@ -260,13 +275,13 @@ class AMIParamConfigurator(HasTraits):
260
275
  @property
261
276
  def info_ami_params(self):
262
277
  "Dictionary of *Reserved* AMI parameter values."
263
- return self._info_dict
278
+ return self._reserved_param_dict
264
279
 
265
280
  def get_init(
266
281
  self,
267
282
  bit_time: float,
268
283
  sample_interval: float,
269
- channel_response: NDArray[float],
284
+ channel_response: NDArray[np.longdouble],
270
285
  ami_params: Optional[dict[str, Any]] = None
271
286
  ) -> AMIModelInitializer:
272
287
  """
@@ -343,14 +358,25 @@ node_name = tap_ix ^ symbol # `tap_ix` is new and gives the tap position; negat
343
358
  def node():
344
359
  "Parse AMI node."
345
360
  yield lparen
346
- label = yield node_name
361
+ label = yield node_name
347
362
  values = yield many(expr)
348
363
  yield rparen
349
364
  return (label, values)
350
365
 
351
366
 
367
+ @generate("AMI file")
368
+ def root():
369
+ "Parse AMI file."
370
+ yield lparen
371
+ label = yield node_name
372
+ values = yield many(node)
373
+ yield rparen
374
+ return (label, values)
375
+
376
+
352
377
  expr = atom | node
353
- ami_defs = ignore >> node
378
+ ami = ignore >> root
379
+ ami_parse: AmiParser = ami.parse
354
380
 
355
381
 
356
382
  def proc_branch(branch):
@@ -428,102 +454,115 @@ def proc_branch(branch):
428
454
  return results
429
455
 
430
456
 
431
- def parse_ami_param_defs(param_str): # pylint: disable=too-many-branches
432
- """Parse the contents of a IBIS-AMI parameter definition file.
457
+ def parse_ami_file_contents( # pylint: disable=too-many-locals,too-many-branches
458
+ file_contents: str
459
+ ) -> tuple[ParseErrMsg, AmiRootName, str, ReservedParamDict, ModelSpecificDict]:
460
+ """
461
+ Parse the contents of an IBIS-AMI *parameter definition* (i.e. - `*.ami`) file.
433
462
 
434
463
  Args:
435
- param_str (str): The contents of the file, as a single string.
464
+ file_contents: The contents of the file, as a single string.
436
465
 
437
466
  Example:
438
467
  ::
439
468
 
440
469
  with open(<ami_file_name>) as ami_file:
441
- param_str = ami_file.read()
442
- (err_str, param_dict) = parse_ami_param_defs(param_str)
470
+ file_contents = ami_file.read()
471
+ (err_str, root_name, reserved_param_dict, model_specific_param_dict) = parse_ami_file_contents(file_contents)
443
472
 
444
473
  Returns:
445
- (str, dict): A pair containing:
446
- err_str:
447
- - None, if parser succeeds.
448
- - Helpful message, if it fails.
449
- param_dict: Dictionary containing parameter definitions.
450
- (Empty, on failure.)
451
- It has a single key, at the top level, which is the
452
- model root name. This key indexes the actual
453
- parameter dictionary, which has the following
454
- structure::
455
-
456
- {
457
- 'description' : <optional model description string>
458
- 'Reserved_Parameters' : <dictionary of reserved parameter defintions>
459
- 'Model_Specific' : <dictionary of model specific parameter definitions>
460
- }
461
-
462
- The keys of the 'Reserved_Parameters' dictionary are
463
- limited to those called out in the IBIS-AMI
464
- specification.
465
-
466
- The keys of the 'Model_Specific' dictionary can be
467
- anything.
468
-
469
- The values of both are either:
470
- - instances of class *AMIParameter*, or
471
- - sub-dictionaries following the same pattern.
474
+ A tuple containing
475
+
476
+ 1. Any error message generated by the parser. (empty on success)
477
+
478
+ 2. AMI file "root" name.
479
+
480
+ 3. *Reserved Parameters* dictionary. (empty on failure)
481
+
482
+ - The keys of the *Reserved Parameters* dictionary are
483
+ limited to those called out in the IBIS-AMI specification.
484
+
485
+ - The values of the *Reserved Parameters* dictionary
486
+ must be instances of class ``AMIParameter``.
487
+
488
+ 4. *Model Specific Parameters* dictionary. (empty on failure)
489
+
490
+ - The keys of the *Model Specific Parameters* dictionary can be anything.
491
+
492
+ - The values of the *Model Specific Parameters* dictionary
493
+ may be either: an instance of class ``AMIParameter``, or a nested sub-dictionary.
472
494
  """
473
495
  try:
474
- res = ami_defs.parse(param_str)
496
+ res = ami_parse(file_contents)
475
497
  except ParseError as pe:
476
- err_str = f"Expected {pe.expected} at {pe.loc()} in:\n{pe.text[pe.index:]}"
477
- return err_str, {}
498
+ err_str = ParseErrMsg(f"Expected {pe.expected} at {pe.loc()} in:\n{pe.text[pe.index:]}")
499
+ return err_str, AmiRootName(""), "", {}, {}
478
500
 
479
501
  err_str, param_dict = proc_branch(res)
480
502
  if err_str:
481
- return (err_str, {"res": res, "dict": param_dict})
503
+ return (err_str, AmiRootName(""), "", {}, {})
504
+ assert len(param_dict.keys()) == 1, ValueError(
505
+ f"Malformed AMI parameter S-exp has top-level keys: {param_dict.keys()}!")
482
506
 
483
507
  reserved_found = False
484
508
  init_returns_impulse_found = False
485
509
  getwave_exists_found = False
486
510
  model_spec_found = False
487
- params = list(param_dict.items())[0][1]
511
+ root_name, params = list(param_dict.items())[0]
512
+ description = ""
513
+ reserved_params_dict = {}
514
+ model_specific_dict = {}
515
+ _err_str = ""
488
516
  for label in list(params.keys()):
517
+ tmp_params = params[label]
489
518
  if label == "Reserved_Parameters":
490
519
  reserved_found = True
491
- tmp_params = params[label]
492
520
  for param_name in list(tmp_params.keys()):
493
- if param_name not in AMIParameter.RESERVED_PARAM_NAMES:
494
- err_str += f"WARNING: Unrecognized reserved parameter name, '{param_name}', found in parameter definition string!\n"
521
+ if param_name not in RESERVED_PARAM_NAMES:
522
+ _err_str += f"WARNING: Unrecognized reserved parameter name, '{param_name}', found in parameter definition string!\n"
495
523
  continue
496
524
  param = tmp_params[param_name]
497
525
  if param.pname == "AMI_Version":
498
526
  if param.pusage != "Info" or param.ptype != "String":
499
- err_str += "WARNING: Malformed 'AMI_Version' parameter.\n"
527
+ _err_str += "WARNING: Malformed 'AMI_Version' parameter.\n"
500
528
  elif param.pname == "Init_Returns_Impulse":
501
529
  init_returns_impulse_found = True
502
530
  elif param.pname == "GetWave_Exists":
503
531
  getwave_exists_found = True
532
+ reserved_params_dict = tmp_params
504
533
  elif label == "Model_Specific":
505
534
  model_spec_found = True
535
+ model_specific_dict = tmp_params
506
536
  elif label == "description":
507
- pass
537
+ description = str(tmp_params)
508
538
  else:
509
- err_str += f"WARNING: Unrecognized group with label, '{label}', found in parameter definition string!\n"
539
+ _err_str += f"WARNING: Unrecognized group with label, '{label}', found in parameter definition string!\n"
510
540
 
511
541
  if not reserved_found:
512
- err_str += "ERROR: Reserved parameters section not found! It is required."
542
+ _err_str += "ERROR: Reserved parameters section not found! It is required."
513
543
 
514
544
  if not init_returns_impulse_found:
515
- err_str += "ERROR: Reserved parameter, 'Init_Returns_Impulse', not found! It is required."
545
+ _err_str += "ERROR: Reserved parameter, 'Init_Returns_Impulse', not found! It is required."
516
546
 
517
547
  if not getwave_exists_found:
518
- err_str += "ERROR: Reserved parameter, 'GetWave_Exists', not found! It is required."
548
+ _err_str += "ERROR: Reserved parameter, 'GetWave_Exists', not found! It is required."
519
549
 
520
550
  if not model_spec_found:
521
- err_str += "WARNING: Model specific parameters section not found!"
551
+ _err_str += "WARNING: Model specific parameters section not found!"
552
+
553
+ return (ParseErrMsg(_err_str), root_name, description, reserved_params_dict, model_specific_dict)
554
+
522
555
 
523
- return (err_str, param_dict)
556
+ # Legacy client code support:
557
+ def parse_ami_param_defs(file_contents: str) -> tuple[ParseErrMsg, dict[str, Any]]:
558
+ "The legacy version of ``parse_ami_file_contents()``."
559
+ err_msg, root_name, description, reserved_params_dict, model_specific_dict = parse_ami_file_contents(file_contents)
560
+ return (err_msg, {root_name: {"description": description,
561
+ "Reserved_Parameters": reserved_params_dict,
562
+ "Model_Specific": model_specific_dict}})
524
563
 
525
564
 
526
- def make_gui(params: Parameters) -> tuple[Group, list[TraitType]]:
565
+ def make_gui(params: ModelSpecificDict) -> tuple[Group, list[TraitType]]:
527
566
  """
528
567
  Builds top-level ``Group`` and list of ``Trait`` s from AMI parameter dictionary.
529
568
 
@@ -0,0 +1,84 @@
1
+ """
2
+ IBIS-AMI reserved parameter names, as a Python ``dataclass``.
3
+
4
+ Original author: David Banas <capn.freako@gmail.com>
5
+
6
+ Original date: March 17, 2025
7
+
8
+ Copyright (c) 2025 David Banas; all rights reserved World wide.
9
+ """
10
+
11
+ from dataclasses import dataclass
12
+
13
+
14
+ @dataclass
15
+ class AmiReservedParameterName():
16
+ "IBIS-AMI Reserved Parameter Name"
17
+
18
+ pname: str
19
+
20
+ def __post_init__(self):
21
+ "Validate parameter name."
22
+
23
+ assert self.pname in RESERVED_PARAM_NAMES, ValueError(
24
+ f"Parameter name: {self.pname}, is not an IBIS-AMI reserved parameter name!")
25
+
26
+
27
+ RESERVED_PARAM_NAMES = [
28
+ "AMI_Version",
29
+ "Init_Returns_Impulse",
30
+ "GetWave_Exists",
31
+ "Use_Init_Output",
32
+ "Max_Init_Aggressors",
33
+ "Ignore_Bits",
34
+ "Resolve_Exists",
35
+ "Model_Name",
36
+ "Special_Param_Names",
37
+ "Component_Name",
38
+ "Signal_Name",
39
+ "Rx_Decision_Time",
40
+ "DC_Offset",
41
+ "Rx_Use_Clock_Input",
42
+ "Supporting_Files",
43
+ "DLL_Path",
44
+ "DLL_ID",
45
+ "Tx_Jitter",
46
+ "Tx_DCD",
47
+ "Tx_Rj",
48
+ "Tx_Dj",
49
+ "Tx_Sj",
50
+ "Tx_Sj_Frequency",
51
+ "Rx_DCD",
52
+ "Rx_Rj",
53
+ "Rx_Dj",
54
+ "Rx_Sj",
55
+ "Rx_Clock_PDF",
56
+ "Rx_Clock_Recovery_Mean",
57
+ "Rx_Clock_Recovery_Rj",
58
+ "Rx_Clock_Recovery_Dj",
59
+ "Rx_Clock_Recovery_Sj",
60
+ "Rx_Clock_Recovery_DCD",
61
+ "Rx_Receiver_Sensitivity",
62
+ "Rx_Noise",
63
+ "Rx_GaussianNoise",
64
+ "Rx_UniformNoise",
65
+ "Modulation",
66
+ "PAM4_Mapping",
67
+ "PAM4_UpperThreshold",
68
+ "PAM4_CenterThreshold",
69
+ "PAM4_LowerThreshold",
70
+ "PAM4_UpperEyeOffset",
71
+ "PAM4_CenterEyeOffset",
72
+ "PAM4_LowerEyeOffset",
73
+ "Repeater_Type",
74
+ "BCI_Protocol",
75
+ "BCI_ID",
76
+ "BCI_State",
77
+ "BCI_Message_Interval_UI",
78
+ "BCI_Training_UI",
79
+ "BCI_Training_Mode",
80
+ "Ts4file",
81
+ "Tx_V",
82
+ "Tx_R",
83
+ "Rx_R",
84
+ ]
@@ -20,8 +20,10 @@ Cvec: TypeAlias = npt.NDArray[Comp]
20
20
  PI: float = 3.141592653589793238462643383279502884
21
21
  TWOPI: float = 2.0 * PI
22
22
 
23
- TestConfig: TypeAlias = tuple[str, tuple[dict[str, Any], dict[str, Any]]]
24
- TestSweep: TypeAlias = tuple[str, str, list[TestConfig]]
23
+ # TestConfig: TypeAlias = tuple[str, tuple[dict[str, Any], dict[str, Any]]]
24
+ # TestSweep: TypeAlias = tuple[str, str, list[TestConfig]]
25
+ TestConfig = tuple[str, tuple[dict[str, Any], dict[str, Any]]]
26
+ TestSweep = tuple[str, str, list[TestConfig]]
25
27
 
26
28
 
27
29
  def deconv_same(y: Rvec, x: Rvec) -> Rvec:
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes