PyIBIS-AMI 6.1.0__tar.gz → 6.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 (48) hide show
  1. {pyibis_ami-6.1.0/src/PyIBIS_AMI.egg-info → pyibis_ami-6.2.0}/PKG-INFO +2 -3
  2. {pyibis_ami-6.1.0 → pyibis_ami-6.2.0}/README.md +1 -2
  3. {pyibis_ami-6.1.0 → pyibis_ami-6.2.0}/pyproject.toml +1 -1
  4. {pyibis_ami-6.1.0 → pyibis_ami-6.2.0/src/PyIBIS_AMI.egg-info}/PKG-INFO +2 -3
  5. {pyibis_ami-6.1.0 → pyibis_ami-6.2.0}/src/pyibisami/ami/config.py +3 -3
  6. {pyibis_ami-6.1.0 → pyibis_ami-6.2.0}/src/pyibisami/ami/model.py +83 -60
  7. {pyibis_ami-6.1.0 → pyibis_ami-6.2.0}/src/pyibisami/ami/parser.py +18 -15
  8. {pyibis_ami-6.1.0 → pyibis_ami-6.2.0}/src/pyibisami/common.py +3 -1
  9. {pyibis_ami-6.1.0 → pyibis_ami-6.2.0}/src/pyibisami/ibis/model.py +0 -4
  10. {pyibis_ami-6.1.0 → pyibis_ami-6.2.0}/src/pyibisami/ibis/parser.py +1 -1
  11. {pyibis_ami-6.1.0 → pyibis_ami-6.2.0}/src/pyibisami/tools/run_tests.py +1 -2
  12. {pyibis_ami-6.1.0 → pyibis_ami-6.2.0}/LICENSE +0 -0
  13. {pyibis_ami-6.1.0 → pyibis_ami-6.2.0}/MANIFEST.in +0 -0
  14. {pyibis_ami-6.1.0 → pyibis_ami-6.2.0}/docs/source/ami.rst +0 -0
  15. {pyibis_ami-6.1.0 → pyibis_ami-6.2.0}/docs/source/conf.py +0 -0
  16. {pyibis_ami-6.1.0 → pyibis_ami-6.2.0}/docs/source/ibis.rst +0 -0
  17. {pyibis_ami-6.1.0 → pyibis_ami-6.2.0}/docs/source/index.rst +0 -0
  18. {pyibis_ami-6.1.0 → pyibis_ami-6.2.0}/docs/source/install.rst +0 -0
  19. {pyibis_ami-6.1.0 → pyibis_ami-6.2.0}/docs/source/modules.rst +0 -0
  20. {pyibis_ami-6.1.0 → pyibis_ami-6.2.0}/docs/source/tools.rst +0 -0
  21. {pyibis_ami-6.1.0 → pyibis_ami-6.2.0}/examples/all_taps.run +0 -0
  22. {pyibis_ami-6.1.0 → pyibis_ami-6.2.0}/examples/bandwidth.run +0 -0
  23. {pyibis_ami-6.1.0 → pyibis_ami-6.2.0}/examples/dummy.em +0 -0
  24. {pyibis_ami-6.1.0 → pyibis_ami-6.2.0}/examples/examples.md +0 -0
  25. {pyibis_ami-6.1.0 → pyibis_ami-6.2.0}/examples/freq_resp.em +0 -0
  26. {pyibis_ami-6.1.0 → pyibis_ami-6.2.0}/examples/impulse.em +0 -0
  27. {pyibis_ami-6.1.0 → pyibis_ami-6.2.0}/examples/impulse_response_8ma.txt +0 -0
  28. {pyibis_ami-6.1.0 → pyibis_ami-6.2.0}/examples/mode.run +0 -0
  29. {pyibis_ami-6.1.0 → pyibis_ami-6.2.0}/examples/stratix4_ami_tx_runs/all_taps.run +0 -0
  30. {pyibis_ami-6.1.0 → pyibis_ami-6.2.0}/examples/stratix4_ami_tx_runs/test_results.xml +0 -0
  31. {pyibis_ami-6.1.0 → pyibis_ami-6.2.0}/examples/test_results/test_results.xsl +0 -0
  32. {pyibis_ami-6.1.0 → pyibis_ami-6.2.0}/setup.cfg +0 -0
  33. {pyibis_ami-6.1.0 → pyibis_ami-6.2.0}/src/PyIBIS_AMI.egg-info/SOURCES.txt +0 -0
  34. {pyibis_ami-6.1.0 → pyibis_ami-6.2.0}/src/PyIBIS_AMI.egg-info/dependency_links.txt +0 -0
  35. {pyibis_ami-6.1.0 → pyibis_ami-6.2.0}/src/PyIBIS_AMI.egg-info/entry_points.txt +0 -0
  36. {pyibis_ami-6.1.0 → pyibis_ami-6.2.0}/src/PyIBIS_AMI.egg-info/requires.txt +0 -0
  37. {pyibis_ami-6.1.0 → pyibis_ami-6.2.0}/src/PyIBIS_AMI.egg-info/top_level.txt +0 -0
  38. {pyibis_ami-6.1.0 → pyibis_ami-6.2.0}/src/pyibisami/__init__.py +0 -0
  39. {pyibis_ami-6.1.0 → pyibis_ami-6.2.0}/src/pyibisami/__main__.py +0 -0
  40. {pyibis_ami-6.1.0 → pyibis_ami-6.2.0}/src/pyibisami/ami/__init__.py +0 -0
  41. {pyibis_ami-6.1.0 → pyibis_ami-6.2.0}/src/pyibisami/ami/generic.ami.em +0 -0
  42. {pyibis_ami-6.1.0 → pyibis_ami-6.2.0}/src/pyibisami/ami/generic.ibs.em +0 -0
  43. {pyibis_ami-6.1.0 → pyibis_ami-6.2.0}/src/pyibisami/ami/parameter.py +0 -0
  44. {pyibis_ami-6.1.0 → pyibis_ami-6.2.0}/src/pyibisami/ibis/__init__.py +0 -0
  45. {pyibis_ami-6.1.0 → pyibis_ami-6.2.0}/src/pyibisami/ibis/file.py +0 -0
  46. {pyibis_ami-6.1.0 → pyibis_ami-6.2.0}/src/pyibisami/tools/__init__.py +0 -0
  47. {pyibis_ami-6.1.0 → pyibis_ami-6.2.0}/src/pyibisami/tools/test_results.xsl +0 -0
  48. {pyibis_ami-6.1.0 → pyibis_ami-6.2.0}/tests/test_run_tests.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: PyIBIS-AMI
3
- Version: 6.1.0
3
+ Version: 6.2.0
4
4
  Summary: Facilitates working directly with IBIS-AMI DLLs from the Python command prompt.
5
5
  Author: David Patterson
6
6
  Author-email: David Banas <capn.freako@gmail.com>
@@ -43,8 +43,7 @@ PyIBIS-AMI is a Python package of tools useful in the development and testing of
43
43
  This library is used in [PyBERT](https://github.com/capn-freako/PyBERT) and also provides two
44
44
  command line applications.
45
45
 
46
- It can be installed via `conda`. PyPi support is experimental but can be installed with
47
- `pip install PyIBIS-AMI`.
46
+ It can be installed via: `pip install PyIBIS-AMI`.
48
47
 
49
48
  [View API/Developer's Documentation.](https://pyibis-ami.readthedocs.io/en/latest/)
50
49
 
@@ -6,8 +6,7 @@ PyIBIS-AMI is a Python package of tools useful in the development and testing of
6
6
  This library is used in [PyBERT](https://github.com/capn-freako/PyBERT) and also provides two
7
7
  command line applications.
8
8
 
9
- It can be installed via `conda`. PyPi support is experimental but can be installed with
10
- `pip install PyIBIS-AMI`.
9
+ It can be installed via: `pip install PyIBIS-AMI`.
11
10
 
12
11
  [View API/Developer's Documentation.](https://pyibis-ami.readthedocs.io/en/latest/)
13
12
 
@@ -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 = "6.1.0"
8
+ version = "6.2.0"
9
9
  authors = [ {name = "David Banas", email = "capn.freako@gmail.com"}
10
10
  , {name = "David Patterson"}
11
11
  ]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: PyIBIS-AMI
3
- Version: 6.1.0
3
+ Version: 6.2.0
4
4
  Summary: Facilitates working directly with IBIS-AMI DLLs from the Python command prompt.
5
5
  Author: David Patterson
6
6
  Author-email: David Banas <capn.freako@gmail.com>
@@ -43,8 +43,7 @@ PyIBIS-AMI is a Python package of tools useful in the development and testing of
43
43
  This library is used in [PyBERT](https://github.com/capn-freako/PyBERT) and also provides two
44
44
  command line applications.
45
45
 
46
- It can be installed via `conda`. PyPi support is experimental but can be installed with
47
- `pip install PyIBIS-AMI`.
46
+ It can be installed via: `pip install PyIBIS-AMI`.
48
47
 
49
48
  [View API/Developer's Documentation.](https://pyibis-ami.readthedocs.io/en/latest/)
50
49
 
@@ -237,9 +237,9 @@ def mk_tests(test_defs, file_base_name, test_dir="test_runs"): # pylint: disabl
237
237
  # and import what you need from this module. This is much cleaner.
238
238
  @click.command(context_settings={"help_option_names": ["-h", "--help"]})
239
239
  @click.argument("py_file", type=click.Path(exists=True, resolve_path=True))
240
- @click.option(
241
- "-d", "--test_dir", show_default=True, default="test_runs", help="Output directory for test run generation."
242
- )
240
+ # @click.option(
241
+ # "-d", "--test_dir", show_default=True, default="test_runs", help="Output directory for test run generation."
242
+ # )
243
243
  @click.version_option()
244
244
  def main(py_file):
245
245
  """Configure IBIS-AMI model C++ source code, IBIS model, and AMI file.
@@ -10,10 +10,10 @@ Copyright (c) 2019 David Banas; All rights reserved World wide.
10
10
  import copy as cp
11
11
  from ctypes import CDLL, byref, c_char_p, c_double # pylint: disable=no-name-in-module
12
12
  from pathlib import Path
13
- from typing import Iterator, Any, Optional
13
+ from typing import Any, Optional
14
14
 
15
15
  import numpy as np
16
- from numpy.random import randint
16
+ # from numpy.random import randint
17
17
 
18
18
  from pyibisami.common import * # pylint: disable=wildcard-import,unused-wildcard-import # noqa: F403
19
19
 
@@ -214,7 +214,7 @@ class AMIModelInitializer:
214
214
 
215
215
  class AMIModel: # pylint: disable=too-many-instance-attributes
216
216
  """
217
- Class defining the structure and behavior of an AMI Model.
217
+ Class defining the structure and behavior of an IBIS-AMI Model.
218
218
 
219
219
  Notes:
220
220
  1. Makes the calling of ``AMI_Close()`` automagic,
@@ -272,14 +272,26 @@ class AMIModel: # pylint: disable=too-many-instance-attributes
272
272
  self._amiClose(self._ami_mem_handle)
273
273
 
274
274
  # Set up the AMI_Init() arguments.
275
- self._channel_response = init_object._init_data["channel_response"] # pylint: disable=protected-access,attribute-defined-outside-init
275
+ self._channel_response = init_object._init_data[ # pylint: disable=protected-access,attribute-defined-outside-init
276
+ "channel_response"
277
+ ]
276
278
  self._initOut = cp.copy(self._channel_response) # type: ignore # pylint: disable=attribute-defined-outside-init
277
- self._row_size = init_object._init_data["row_size"] # pylint: disable=protected-access,attribute-defined-outside-init
278
- self._num_aggressors = init_object._init_data["num_aggressors"] # pylint: disable=protected-access,attribute-defined-outside-init
279
- self._sample_interval = init_object._init_data["sample_interval"] # pylint: disable=protected-access,attribute-defined-outside-init
280
- self._bit_time = init_object._init_data["bit_time"] # pylint: disable=protected-access,attribute-defined-outside-init
279
+ self._row_size = init_object._init_data[ # pylint: disable=protected-access,attribute-defined-outside-init
280
+ "row_size"
281
+ ]
282
+ self._num_aggressors = init_object._init_data[ # pylint: disable=protected-access,attribute-defined-outside-init
283
+ "num_aggressors"
284
+ ]
285
+ self._sample_interval = init_object._init_data[ # pylint: disable=protected-access,attribute-defined-outside-init
286
+ "sample_interval"
287
+ ]
288
+ self._bit_time = init_object._init_data[ # pylint: disable=protected-access,attribute-defined-outside-init
289
+ "bit_time"
290
+ ]
281
291
  self._info_params = init_object.info_params # pylint: disable=attribute-defined-outside-init
282
- assert self._info_params, RuntimeError(f"`info_params` is None!\n`init_object: {init_object}") # pylint: disable=attribute-defined-outside-init
292
+ assert self._info_params, RuntimeError(
293
+ f"`info_params` is None!\n`init_object: {init_object}"
294
+ ) # pylint: disable=attribute-defined-outside-init
283
295
 
284
296
  # Check GetWave() consistency if possible.
285
297
  if init_object.info_params and init_object.info_params["GetWave_Exists"]:
@@ -342,15 +354,18 @@ class AMIModel: # pylint: disable=too-many-instance-attributes
342
354
  # Initialize attributes used by getWave().
343
355
  bit_time = init_object.bit_time
344
356
  sample_interval = init_object.sample_interval
345
- # ToDo: Fix this.
357
+ # ToDo: Fix this. There isn't actually a requirement that `bit_time` be an integral multiple of `sample_interval`.
358
+ # And there may be an advantage to having it not be!
346
359
  if (bit_time % sample_interval) > (sample_interval / 100):
347
360
  raise ValueError(
348
- f"Bit time ({bit_time * 1e9 : 6.3G} ns) must be an integral multiple of sample interval ({sample_interval * 1e9 : 6.3G} ns)."
361
+ f"Bit time ({bit_time * 1e9: 6.3G} ns) must be an integral multiple of sample interval ({sample_interval * 1e9: 6.3G} ns)."
349
362
  )
350
363
  self._samps_per_bit = int(bit_time / sample_interval) # pylint: disable=attribute-defined-outside-init
351
- self._bits_per_call = init_object.row_size / self._samps_per_bit # pylint: disable=attribute-defined-outside-init
364
+ self._bits_per_call = ( # pylint: disable=attribute-defined-outside-init
365
+ init_object.row_size / self._samps_per_bit
366
+ )
352
367
 
353
- def getWave(self, wave: Rvec, bits_per_call: int = 0) -> tuple[Rvec, Rvec]: # noqa: F405
368
+ def getWave(self, wave: Rvec, bits_per_call: int = 0) -> tuple[Rvec, Rvec, list[str]]: # noqa: F405
354
369
  """
355
370
  Performs time domain processing of input waveform, using the
356
371
  ``AMI_GetWave()`` function.
@@ -363,9 +378,10 @@ class AMIModel: # pylint: disable=too-many-instance-attributes
363
378
  Default: 0 (Means "Use existing value.")
364
379
 
365
380
  Returns:
366
- (wave_out, clock_times): A pair containing:
367
- - the processed waveform, and
368
- - the recovered slicer sampling instants.
381
+ (wave_out, clock_times, params_out): A tuple containing:
382
+ - the processed waveform,
383
+ - the recovered slicer sampling instants, and
384
+ - the list of output parameter strings received from each call to ``AMI_GetWave()``.
369
385
 
370
386
  Notes:
371
387
  1. The returned clock times are given in "pre-edge-aligned" fashion,
@@ -379,12 +395,13 @@ class AMIModel: # pylint: disable=too-many-instance-attributes
379
395
 
380
396
  # Create the required C types.
381
397
  Signal = c_double * samps_per_call
382
- Clocks = c_double * bits_per_call
398
+ Clocks = c_double * (bits_per_call + 1) # The "+1" is critical, to prevent access violations by the model.
383
399
 
384
400
  idx = 0 # Holds the starting index of the next processing chunk.
385
401
  _clock_times = Clocks(0.0)
386
402
  wave_out: list[float] = []
387
403
  clock_times: list[float] = []
404
+ params_out: list[str] = []
388
405
  input_len = len(wave)
389
406
  while idx < input_len:
390
407
  remaining_samps = input_len - idx
@@ -394,18 +411,24 @@ class AMIModel: # pylint: disable=too-many-instance-attributes
394
411
  else:
395
412
  tmp_wave = wave[idx: idx + samps_per_call]
396
413
  _wave = Signal(*tmp_wave)
397
- self._amiGetWave(byref(_wave), len(_wave), byref(_clock_times),
398
- byref(self._ami_params_out), self._ami_mem_handle) # type: ignore
414
+ self._amiGetWave(
415
+ byref(_wave), len(_wave), byref(_clock_times), byref(self._ami_params_out), self._ami_mem_handle
416
+ ) # type: ignore
399
417
  wave_out.extend(_wave)
400
418
  clock_times.extend(_clock_times)
419
+ params_out.append(self.ami_params_out)
401
420
  idx += len(_wave)
402
421
 
403
- return np.array(wave_out), np.array(clock_times[: len(wave_out) // self._samps_per_bit])
422
+ return np.array(wave_out), np.array(clock_times[: len(wave_out) // self._samps_per_bit]), params_out
404
423
 
405
- def get_responses(self, bits_per_call: int = 0, bit_gen: Optional[Iterator[int]] = None # pylint: disable=too-many-locals
406
- ) -> dict[str, Any]:
424
+ def get_responses( # pylint: disable=too-many-locals
425
+ self,
426
+ bits_per_call: int = 0,
427
+ # bit_gen: Optional[Iterator[int]] = None,
428
+ pad_bits: int = 10,
429
+ ) -> dict[str, Any]:
407
430
  """
408
- Get the impulse, step, pulse, and frequency responses of an initialized IBIS-AMI model.
431
+ Get the impulse response of an initialized IBIS-AMI model, alone and convolved with the channel.
409
432
 
410
433
  Keyword Args:
411
434
  bits_per_call: Number of bits to include in the input to `GetWave()`.
@@ -413,17 +436,16 @@ class AMIModel: # pylint: disable=too-many-instance-attributes
413
436
  bit_gen: The bit generator to use for `GetWave()` input data generation.
414
437
  Should produce integers from the set: {0,1}.
415
438
  Default: None (Means "use randint(2)".)
439
+ pad_bits: Number of bits to pad leading edge with when calling `GetWave()`,
440
+ to protect from initial garbage in `GetWave()` output.
441
+ Default: 10
416
442
 
417
443
  Returns:
418
- Dictionary containing the responses under the following keys:
444
+ rslt: Dictionary containing the responses under the following keys:
419
445
  "imp_resp_init": The model's impulse response, from its `AMI_Init()` function (V/sample).
420
- "step_resp_init": The model's step response, from its `AMI_Init()` function.
421
- "pulse_resp_init": The model's pulse response, from its `AMI_Init()` function.
422
- "freq_resp_init": The model's frequency response, from its `AMI_Init()` function.
446
+ "out_resp_init": `imp_resp_init` convolved with the channel.
423
447
  "imp_resp_getw": The model's impulse response, from its `AMI_GetWave()` function (V/sample).
424
- "step_resp_getw": The model's step response, from its `AMI_GetWave()` function.
425
- "pulse_resp_getw": The model's pulse response, from its `AMI_GetWave()` function.
426
- "freq_resp_getw": The model's frequency response, from its `AMI_GetWave()` function.
448
+ "out_resp_getw": `imp_resp_getw` convolved with the channel.
427
449
 
428
450
  Notes:
429
451
  1. If either set of keys (i.e. - "..._init" or "..._getw")
@@ -432,12 +454,11 @@ class AMIModel: # pylint: disable=too-many-instance-attributes
432
454
  was not available in the given model.
433
455
  2. An empty dictionary implies that neither the `Init_Returns_Impulse`
434
456
  nor the `GetWave_Exists` AMI reserved parameter was True.
435
- 3. One additional key is added to the returned dictionary, when appropriate:
436
- - "params_out_getw": If the model's ``AMI_GetWave()`` function is called and it returns parameters.
437
- 4. Note that impulse responses are returned with units: (V/sample), not (V/s).
438
- 5. The frequency vector corresponding to the returned frequency responses is given by:
439
- [n * f0 for n in range(impulse_length // 2 + 1)], where:
440
- f0 = 1 / (ts * impulse_length)
457
+ 3. Note that impulse responses are returned with units: (V/sample), not (V/s).
458
+
459
+ ToDo:
460
+ 1. Implement `bit_gen`.
461
+ 2. Implement `ignore_bits`.
441
462
  """
442
463
 
443
464
  rslt = {}
@@ -446,51 +467,51 @@ class AMIModel: # pylint: disable=too-many-instance-attributes
446
467
  impulse_length = self.row_size
447
468
  ui = self.bit_time
448
469
  ts = self.sample_interval
449
- nspui = int(ui / ts)
450
- ignore_bits = self.info_params["Ignore_Bits"]
470
+ # info_params = self.info_params
471
+ # ignore_bits = info_params["Ignore_Bits"] if "Ignore_Bits" in info_params else 0
451
472
 
452
473
  # Capture/convert instance variables.
453
474
  chnl_imp = np.array(self.channel_response) * ts # input (a.k.a. - "channel") impulse response (V/sample)
454
475
  out_imp = np.array(self.initOut) * ts # output impulse response (V/sample)
455
476
 
477
+ # Calculate some needed intermediate values.
478
+ nspui = int(ui / ts) # samps per UI
479
+ pad_samps = pad_bits * nspui # leading edge padding samples for GetWave() calls
480
+
456
481
  # Extract and return the model responses.
457
482
  if self.info_params["Init_Returns_Impulse"]:
458
483
  h_model = deconv_same(out_imp, chnl_imp) # noqa: F405
459
484
  # h_model = irfft(rfft(out_imp) / rfft(chnl_imp)) # Seems to produce results just as noisy as above.
460
485
  h_model = np.where(abs(h_model) > 1, np.zeros(len(h_model)), h_model)
461
486
  rslt["imp_resp_init"] = np.roll(h_model, -len(h_model) // 2 + 3 * nspui)
462
- rslt["step_resp_init"] = np.cumsum(rslt["imp_resp_init"])
463
- rslt["pulse_resp_init"] = rslt["step_resp_init"] - np.pad(
464
- rslt["step_resp_init"][:-nspui], (nspui, 0), mode="constant", constant_values=0
465
- )
466
- rslt["freq_resp_init"] = np.fft.rfft(rslt["imp_resp_init"])
467
487
  rslt["out_resp_init"] = out_imp
468
488
  if self.info_params["GetWave_Exists"]:
469
489
  # Run `ignore_bits` bits of data through the model first.
470
- if bit_gen:
471
- wave_in = [next(bit_gen) for _ in range(ignore_bits)]
472
- else:
473
- wave_in = [randint(2) for _ in range(ignore_bits)]
474
- _, _ = self.getWave(np.array(wave_in) - 0.5, bits_per_call=bits_per_call)
490
+ # if bit_gen:
491
+ # wave_in = [next(bit_gen) for _ in range(ignore_bits)]
492
+ # else:
493
+ # wave_in = [randint(2) for _ in range(ignore_bits)]
494
+ # _, _, _ = self.getWave(np.array(wave_in) - 0.5, bits_per_call=bits_per_call)
495
+
475
496
  # Then, run a perfect step, to extract model's step response.
476
- wave_out, _ = self.getWave(
477
- np.array([0] * impulse_length + [1] * impulse_length) - 0.5, bits_per_call=bits_per_call
497
+ wave_out, _, _ = self.getWave(
498
+ np.array([0] * pad_samps + [1] * impulse_length) - 0.5,
499
+ bits_per_call=bits_per_call,
478
500
  )
479
- # Remove any artifactual vertical offset from beginning of result:
480
- rslt["step_resp_getw"] = wave_out[impulse_length:] - wave_out[impulse_length - 1]
501
+
481
502
  # Calculate other responses from step response.
482
- rslt["imp_resp_getw"] = np.pad(np.diff(rslt["step_resp_getw"]), (1, 0), mode="constant", constant_values=0)
483
- rslt["pulse_resp_getw"] = rslt["step_resp_getw"] - np.pad(
484
- rslt["step_resp_getw"][:-nspui], (nspui, 0), mode="constant", constant_values=0
485
- )
486
- rslt["freq_resp_getw"] = np.fft.rfft(rslt["imp_resp_getw"])
503
+ rslt["imp_resp_getw"] = np.diff(wave_out[pad_samps:])
504
+
487
505
  # Calculate effective cumulative impulse response (i.e. - channel + Tx).
488
506
  # - Form the step response equivalent to the given channel impulse response.
489
507
  chnl_step = np.cumsum(chnl_imp)
490
508
  # - And run it through `GetWave()`, after d.c. balancing.
491
- out_step, _ = self.getWave(chnl_step - chnl_step.ptp() / 2, bits_per_call=bits_per_call)
509
+ chnl_step_bal = chnl_step - chnl_step[-1] / 2
510
+ out_step, _, _ = self.getWave(
511
+ np.pad(chnl_step_bal, (pad_samps, 0), mode="edge"), bits_per_call=bits_per_call
512
+ )
492
513
  # - Convert result back to an impulse response.
493
- rslt["out_resp_getw"] = np.pad(np.diff(out_step), (1, 0), mode="constant", constant_values=0)
514
+ rslt["out_resp_getw"] = np.diff(out_step[pad_samps:])
494
515
  return rslt
495
516
 
496
517
  def _getInitOut(self):
@@ -533,7 +554,9 @@ class AMIModel: # pylint: disable=too-many-instance-attributes
533
554
  def _getAmiParamsOut(self):
534
555
  return self._ami_params_out.value
535
556
 
536
- ami_params_out = property(_getAmiParamsOut, doc="The AMI parameter string returned by AMI_Init().")
557
+ ami_params_out = property(
558
+ _getAmiParamsOut, doc="The AMI parameter string returned by either `AMI_Init()` or `AMI_GetWave()`."
559
+ )
537
560
 
538
561
  def _getMsg(self):
539
562
  return self._msg.value
@@ -9,7 +9,7 @@ Copyright (c) 2019 David Banas; all rights reserved World wide.
9
9
 
10
10
  import re
11
11
 
12
- from parsec import ParseError, generate, many, many1, regex, string
12
+ from parsec import ParseError, generate, many, regex, string
13
13
  from traits.api import Bool, Enum, HasTraits, Range, Trait
14
14
  from traitsui.api import Group, Item, View
15
15
  from traitsui.menu import ModalButtons
@@ -210,7 +210,9 @@ class AMIParamConfigurator(HasTraits):
210
210
  # See the docs on the *HasTraits* class, if this is confusing.
211
211
  try: # Querry for a mapped trait, first, by trying to get '<trait_name>_'. (Note the underscore.)
212
212
  res[pname] = self.get(pname + "_")[pname + "_"]
213
- except Exception: # If we get an exception, we have an ordinary (i.e. - not mapped) trait. # pylint: disable=broad-exception-caught
213
+ except (
214
+ Exception # pylint: disable=broad-exception-caught
215
+ ): # If we get an exception, we have an ordinary (i.e. - not mapped) trait.
214
216
  res[pname] = self.get(pname)[pname]
215
217
  elif isinstance(param, dict): # We received a dictionary of subparameters, in 'param'.
216
218
  subs = {}
@@ -270,7 +272,7 @@ def node():
270
272
  "Parse AMI node."
271
273
  yield lparen
272
274
  label = yield node_name
273
- values = yield many1(expr)
275
+ values = yield many(expr)
274
276
  yield rparen
275
277
  return (label, values)
276
278
 
@@ -300,12 +302,9 @@ def proc_branch(branch):
300
302
 
301
303
  Returns:
302
304
  (str, dict): A pair containing:
303
-
304
- err_str:
305
- String containing any errors or warnings encountered,
305
+ err_str: String containing any errors or warnings encountered,
306
306
  while building the parameter dictionary.
307
- param_dict:
308
- Resultant parameter dictionary.
307
+ param_dict: Resultant parameter dictionary.
309
308
  """
310
309
  results = ("", {}) # Empty Results
311
310
  if len(branch) != 2:
@@ -324,9 +323,13 @@ def proc_branch(branch):
324
323
 
325
324
  try:
326
325
  if (
327
- (len(param_tags) > 1) # noqa: W503
328
- and (param_tags[0][0] in AMIParameter._param_def_tag_procs) # pylint: disable=protected-access # noqa: W503
329
- and (param_tags[1][0] in AMIParameter._param_def_tag_procs) # pylint: disable=protected-access # noqa: W503
326
+ (len(param_tags) > 1)
327
+ and ( # noqa: W503
328
+ param_tags[0][0] in AMIParameter._param_def_tag_procs # pylint: disable=protected-access # noqa: W503
329
+ )
330
+ and ( # noqa: W503
331
+ param_tags[1][0] in AMIParameter._param_def_tag_procs # pylint: disable=protected-access # noqa: W503
332
+ )
330
333
  ):
331
334
  try:
332
335
  results = ("", {param_name: AMIParameter(param_name, param_tags)})
@@ -368,12 +371,10 @@ def parse_ami_param_defs(param_str): # pylint: disable=too-many-branches
368
371
 
369
372
  Returns:
370
373
  (str, dict): A pair containing:
371
-
372
374
  err_str:
373
375
  - None, if parser succeeds.
374
376
  - Helpful message, if it fails.
375
- param_dict:
376
- Dictionary containing parameter definitions.
377
+ param_dict: Dictionary containing parameter definitions.
377
378
  (Empty, on failure.)
378
379
  It has a single key, at the top level, which is the
379
380
  model root name. This key indexes the actual
@@ -450,7 +451,9 @@ def parse_ami_param_defs(param_str): # pylint: disable=too-many-branches
450
451
  return (err_str, param_dict)
451
452
 
452
453
 
453
- def make_gui_items(pname, param, first_call=False): # pylint: disable=too-many-locals,too-many-branches,too-many-statements
454
+ def make_gui_items(
455
+ pname, param, first_call=False
456
+ ): # pylint: disable=too-many-locals,too-many-branches,too-many-statements
454
457
  """Builds list of GUI items from AMI parameter dictionary."""
455
458
 
456
459
  gui_items = []
@@ -8,7 +8,7 @@ Original date: May 15, 2024
8
8
  Copyright (c) 2024 David Banas; all rights reserved World wide.
9
9
  """
10
10
 
11
- from typing import TypeAlias, TypeVar
11
+ from typing_extensions import TypeAlias, TypeVar
12
12
 
13
13
  import numpy.typing as npt # type: ignore
14
14
  from scipy.linalg import convolution_matrix, lstsq
@@ -17,6 +17,8 @@ Real = TypeVar("Real", float, float)
17
17
  Comp = TypeVar("Comp", complex, complex)
18
18
  Rvec: TypeAlias = npt.NDArray[Real]
19
19
  Cvec: TypeAlias = npt.NDArray[Comp]
20
+ # Rvec = npt.NDArray[Real]
21
+ # Cvec = npt.NDArray[Comp]
20
22
 
21
23
  # PI: float = np.pi # Causes a failed import of `pyibisami.ami.model` during `tox -e docs`, due to:
22
24
  # File "C:\Users\davibana\prj\PyBERT\PyAMI\src\pyibisami\common.py", line 23, in <module>
@@ -258,10 +258,6 @@ class Model(HasTraits): # pylint: disable=too-many-instance-attributes
258
258
  ((os, _), _) = x
259
259
  return os.lower() == "windows"
260
260
 
261
- # def showExec(x):
262
- # ((os, b), fs) = x
263
- # return os + str(b) + ": " + str(fs)
264
-
265
261
  def partition(p, xs):
266
262
  ts, fs = [], []
267
263
  for x in xs:
@@ -59,7 +59,7 @@ def logf(p, preStr=""):
59
59
  res = p(txt, ix)
60
60
  if not res.status:
61
61
  print(
62
- f"{preStr}: Expected {res.expected} in '{txt[res.index : res.index+5]}' at {ParseError.loc_info(txt, res.index)}."
62
+ f"{preStr}: Expected {res.expected} in '{txt[res.index: res.index + 5]}' at {ParseError.loc_info(txt, res.index)}."
63
63
  )
64
64
  return res
65
65
 
@@ -199,8 +199,7 @@ def run_tests(**kwargs): # pylint: disable=too-many-locals
199
199
  print(f"Please, open file, `{xml_filename}` in a Web browser, in order to view the test results.")
200
200
 
201
201
 
202
- @click.command(context_settings={"ignore_unknown_options": True,
203
- "help_option_names": ["-h", "--help"]})
202
+ @click.command(context_settings={"ignore_unknown_options": True, "help_option_names": ["-h", "--help"]})
204
203
  @click.option(
205
204
  "--model", "-m", default="libami.so", type=click.Path(exists=True), help="Sets the AMI model DLL file name."
206
205
  )
File without changes
File without changes
File without changes
File without changes
File without changes