From b787b6fb7d6785c97f9f6a06231f3c3f963f6f25 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <aromaniello@ntia.gov> Date: Thu, 5 Oct 2023 10:43:06 -0600 Subject: [PATCH 01/13] Apply threading lock to entire IQ capture method --- src/scos_tekrsa/hardware/tekrsa_sigan.py | 201 ++++++++++++----------- 1 file changed, 101 insertions(+), 100 deletions(-) diff --git a/src/scos_tekrsa/hardware/tekrsa_sigan.py b/src/scos_tekrsa/hardware/tekrsa_sigan.py index 3225d39..af5b463 100644 --- a/src/scos_tekrsa/hardware/tekrsa_sigan.py +++ b/src/scos_tekrsa/hardware/tekrsa_sigan.py @@ -13,8 +13,6 @@ logger = logging.getLogger(__name__) -sigan_lock = threading.Lock() - class TekRSASigan(SignalAnalyzerInterface): def __init__(self): @@ -255,109 +253,112 @@ def acquire_time_domain_samples( cal_adjust: bool = True, ): """Acquire specific number of time-domain IQ samples.""" - self._capture_time = None - if isinstance(num_samples, int) or ( - isinstance(num_samples, float) and num_samples.is_integer() - ): - nsamps_req = int(num_samples) # Requested number of samples - else: - raise ValueError("Requested number of samples must be an integer.") - nskip = int(num_samples_skip) # Requested number of samples to skip - nsamps = nsamps_req + nskip # Total number of samples to collect - - if cal_adjust: - # Get calibration data for acquisition - if not (settings.RUNNING_TESTS or settings.MOCK_SIGAN): - cal_params = sensor_calibration.calibration_parameters + with threading.Lock(): + self._capture_time = None + if isinstance(num_samples, int) or ( + isinstance(num_samples, float) and num_samples.is_integer() + ): + nsamps_req = int(num_samples) # Requested number of samples else: - # Make it work for mock sigan/testing. Just match frequency. - cal_params = [vars(self)["_frequency"]] - try: - cal_args = [vars(self)[f"_{p}"] for p in cal_params] - except KeyError: - raise Exception( - "One or more required cal parameters is not a valid sigan setting." - ) - logger.debug(f"Matched calibration params: {cal_args}") - self.recompute_sensor_calibration_data(cal_args) - # Compute the linear gain - db_gain = self.sensor_calibration_data["gain"] - linear_gain = 10.0 ** (db_gain / 20.0) - else: - linear_gain = 1 + raise ValueError("Requested number of samples must be an integer.") + nskip = int(num_samples_skip) # Requested number of samples to skip + nsamps = nsamps_req + nskip # Total number of samples to collect + + if cal_adjust: + # Get calibration data for acquisition + if not (settings.RUNNING_TESTS or settings.MOCK_SIGAN): + cal_params = sensor_calibration.calibration_parameters + else: + # Make it work for mock sigan/testing. Just match frequency. + cal_params = [vars(self)["_frequency"]] + try: + cal_args = [vars(self)[f"_{p}"] for p in cal_params] + except KeyError: + raise Exception( + "One or more required cal parameters is not a valid sigan setting." + ) + logger.debug(f"Matched calibration params: {cal_args}") + self.recompute_sensor_calibration_data(cal_args) + # Compute the linear gain + db_gain = self.sensor_calibration_data["gain"] + linear_gain = 10.0 ** (db_gain / 20.0) + else: + linear_gain = 1 - # Determine correct time length (round up, integer ms) - durationMsec = int(1000 * (nsamps / self.sample_rate)) + ( - 1000 * nsamps % self.sample_rate > 0 - ) + # Determine correct time length (round up, integer ms) + durationMsec = int(1000 * (nsamps / self.sample_rate)) + ( + 1000 * nsamps % self.sample_rate > 0 + ) - if durationMsec == 0: - # Num. samples requested is less than minimum duration for IQ stream. - # Handle this by skipping more samples than requested - durationMsec = 1 # Minimum allowed IQ stream duration - nskip = int((self.sample_rate / 1000) - nsamps_req) - nsamps = nskip + nsamps_req + if durationMsec == 0: + # Num. samples requested is less than minimum duration for IQ stream. + # Handle this by skipping more samples than requested + durationMsec = 1 # Minimum allowed IQ stream duration + nskip = int((self.sample_rate / 1000) - nsamps_req) + nsamps = nskip + nsamps_req - logger.debug(f"acquire_time_domain_samples starting, num_samples = {nsamps}") - logger.debug(f"Number of retries = {retries}") + logger.debug( + f"acquire_time_domain_samples starting, num_samples = {nsamps}" + ) + logger.debug(f"Number of retries = {retries}") - max_retries = retries + max_retries = retries - while True: - self._capture_time = utils.get_datetime_str_now() - with sigan_lock: + while True: + self._capture_time = utils.get_datetime_str_now() data, status = self.rsa.IQSTREAM_Tempfile_NoConfig(durationMsec, True) - - data = data[nskip : nskip + nsamps_req] # Remove extra samples, if any - data_len = len(data) - - logger.debug(f"IQ Stream status: {status}") - - # Check status string for overload / data loss - self.overload = False - if "Input overrange" in status: - self.overload = True - logger.warning("IQ stream: ADC overrange event occurred.") - - if "data loss" in status or "discontinuity" in status: # Invalid data - if retries > 0: - logger.warning( - f"Data loss occurred during IQ streaming. Retrying {retries} more times." - ) - retries -= 1 - continue - else: - err = "Data loss occurred with no retries remaining." - err += f" (tried {max_retries} times.)" - raise RuntimeError(err) - elif ( - not data_len == nsamps_req - ): # Invalid data: incorrect number of samples - if retries > 0: - msg = f"RSA error: requested {nsamps_req + nskip} samples, but got {data_len}." - logger.warning(msg) - logger.warning(f"Retrying {retries} more times.") - retries -= 1 - continue + data = data[nskip : nskip + nsamps_req] # Remove extra samples, if any + data_len = len(data) + + logger.debug(f"IQ Stream status: {status}") + + # Check status string for overload / data loss + self.overload = False + if "Input overrange" in status: + self.overload = True + logger.warning("IQ stream: ADC overrange event occurred.") + + if "data loss" in status or "discontinuity" in status: # Invalid data + if retries > 0: + logger.warning( + f"Data loss occurred during IQ streaming. Retrying {retries} more times." + ) + retries -= 1 + continue + else: + err = "Data loss occurred with no retries remaining." + err += f" (tried {max_retries} times.)" + raise RuntimeError(err) + elif ( + not data_len == nsamps_req + ): # Invalid data: incorrect number of samples + if retries > 0: + msg = f"RSA error: requested {nsamps_req + nskip} samples, but got {data_len}." + logger.warning(msg) + logger.warning(f"Retrying {retries} more times.") + retries -= 1 + continue + else: + err = "Failed to acquire correct number of samples " + err += f"{max_retries} times in a row." + raise RuntimeError(err) else: - err = "Failed to acquire correct number of samples " - err += f"{max_retries} times in a row." - raise RuntimeError(err) - else: - logger.debug(f"IQ stream: successfully acquired {data_len} samples.") - # Scale data to RF power and return - logger.debug(f"Applying gain of {linear_gain}") - data /= linear_gain - - measurement_result = { - "data": data, - "overload": self.overload, - "frequency": self.frequency, - "reference_level": self.reference_level, - "sample_rate": self.rsa.IQSTREAM_GetAcqParameters()[1], - "capture_time": self._capture_time, - } - if self.device_name not in ["RSA306B", "RSA306"]: - measurement_result["attenuation"] = self.attenuation - measurement_result["preamp_enable"] = self.preamp_enable - return measurement_result + logger.debug( + f"IQ stream: successfully acquired {data_len} samples." + ) + # Scale data to RF power and return + logger.debug(f"Applying gain of {linear_gain}") + data /= linear_gain + + measurement_result = { + "data": data, + "overload": self.overload, + "frequency": self.frequency, + "reference_level": self.reference_level, + "sample_rate": self.rsa.IQSTREAM_GetAcqParameters()[1], + "capture_time": self._capture_time, + } + if self.device_name not in ["RSA306B", "RSA306"]: + measurement_result["attenuation"] = self.attenuation + measurement_result["preamp_enable"] = self.preamp_enable + return measurement_result From 531905e96e4b95f2fcf29f462c09da3a736d0815 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <aromaniello@ntia.gov> Date: Thu, 5 Oct 2023 10:43:21 -0600 Subject: [PATCH 02/13] Update pre-commit hooks --- .pre-commit-config.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d9d6576..6aefc0a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -18,7 +18,7 @@ repos: - id: end-of-file-fixer - id: trailing-whitespace - repo: https://github.com/asottile/pyupgrade - rev: v3.10.1 + rev: v3.14.0 hooks: - id: pyupgrade args: ["--py38-plus"] @@ -30,12 +30,12 @@ repos: types: [file, python] args: ["--profile", "black", "--filter-files", "--gitignore"] - repo: https://github.com/psf/black - rev: 23.7.0 + rev: 23.9.1 hooks: - id: black types: [file, python] - repo: https://github.com/igorshubovych/markdownlint-cli - rev: v0.35.0 + rev: v0.37.0 hooks: - id: markdownlint types: [file, markdown] From c2f934d967f827ad8fd241c0132008732069f3ef Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <aromaniello@ntia.gov> Date: Thu, 5 Oct 2023 10:44:27 -0600 Subject: [PATCH 03/13] Update package version --- src/scos_tekrsa/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/scos_tekrsa/__init__.py b/src/scos_tekrsa/__init__.py index f749372..1fe90f6 100644 --- a/src/scos_tekrsa/__init__.py +++ b/src/scos_tekrsa/__init__.py @@ -1 +1 @@ -__version__ = "3.1.3" +__version__ = "3.1.4" From ab2b5736933992b3512894a7505fe577a46783e0 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <aromaniello@ntia.gov> Date: Thu, 5 Oct 2023 10:47:10 -0600 Subject: [PATCH 04/13] name lock object --- src/scos_tekrsa/hardware/tekrsa_sigan.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/scos_tekrsa/hardware/tekrsa_sigan.py b/src/scos_tekrsa/hardware/tekrsa_sigan.py index af5b463..b7dff93 100644 --- a/src/scos_tekrsa/hardware/tekrsa_sigan.py +++ b/src/scos_tekrsa/hardware/tekrsa_sigan.py @@ -253,7 +253,8 @@ def acquire_time_domain_samples( cal_adjust: bool = True, ): """Acquire specific number of time-domain IQ samples.""" - with threading.Lock(): + iq_capture_lock = threading.Lock() + with iq_capture_lock: self._capture_time = None if isinstance(num_samples, int) or ( isinstance(num_samples, float) and num_samples.is_integer() From e51f6e3f1f4ae19d7c3896656c276a6941614148 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <aromaniello@ntia.gov> Date: Fri, 6 Oct 2023 07:44:31 -0600 Subject: [PATCH 05/13] Fix creation of sigan_lock --- src/scos_tekrsa/hardware/tekrsa_sigan.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/scos_tekrsa/hardware/tekrsa_sigan.py b/src/scos_tekrsa/hardware/tekrsa_sigan.py index b7dff93..b984a58 100644 --- a/src/scos_tekrsa/hardware/tekrsa_sigan.py +++ b/src/scos_tekrsa/hardware/tekrsa_sigan.py @@ -13,6 +13,8 @@ logger = logging.getLogger(__name__) +sigan_lock = threading.Lock() + class TekRSASigan(SignalAnalyzerInterface): def __init__(self): @@ -253,8 +255,7 @@ def acquire_time_domain_samples( cal_adjust: bool = True, ): """Acquire specific number of time-domain IQ samples.""" - iq_capture_lock = threading.Lock() - with iq_capture_lock: + with sigan_lock: self._capture_time = None if isinstance(num_samples, int) or ( isinstance(num_samples, float) and num_samples.is_integer() From 657e71a53883b3654cd96589781590dc8ae30cea Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <aromaniello@ntia.gov> Date: Wed, 18 Oct 2023 15:56:32 -0600 Subject: [PATCH 06/13] Update pre-commit hooks --- .pre-commit-config.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6aefc0a..6d0c9f3 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,7 +2,7 @@ default_language_version: python: python3.8 repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 + rev: v4.5.0 hooks: - id: check-ast types: [file, python] @@ -18,7 +18,7 @@ repos: - id: end-of-file-fixer - id: trailing-whitespace - repo: https://github.com/asottile/pyupgrade - rev: v3.14.0 + rev: v3.15.0 hooks: - id: pyupgrade args: ["--py38-plus"] @@ -30,7 +30,7 @@ repos: types: [file, python] args: ["--profile", "black", "--filter-files", "--gitignore"] - repo: https://github.com/psf/black - rev: 23.9.1 + rev: 23.10.0 hooks: - id: black types: [file, python] From 4ae5c5a7c750b634f3afae60737bd2a6f389096f Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <aromaniello@ntia.gov> Date: Wed, 18 Oct 2023 15:58:05 -0600 Subject: [PATCH 07/13] Add plugin version as instance variable in SiganInterface --- src/scos_tekrsa/hardware/tekrsa_sigan.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/scos_tekrsa/hardware/tekrsa_sigan.py b/src/scos_tekrsa/hardware/tekrsa_sigan.py index b984a58..cd82bce 100644 --- a/src/scos_tekrsa/hardware/tekrsa_sigan.py +++ b/src/scos_tekrsa/hardware/tekrsa_sigan.py @@ -8,6 +8,7 @@ ) import scos_tekrsa.hardware.tekrsa_constants as rsa_constants +from scos_tekrsa import __version__ as SCOS_TEKRSA_PLUGIN_VERSION from scos_tekrsa import settings from scos_tekrsa.hardware.mocks.rsa_block import MockRSA @@ -21,6 +22,7 @@ def __init__(self): try: super().__init__() logger.info("Initializing Tektronix RSA Signal Analyzer") + self.plugin_version = SCOS_TEKRSA_PLUGIN_VERSION self.rsa = None self._is_available = False # should not be set outside of connect method From 21c96ccb3d58042475bf5c00fb39ae5e40b9b30b Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <aromaniello@ntia.gov> Date: Thu, 19 Oct 2023 12:05:17 -0400 Subject: [PATCH 08/13] Add plugin_version property to sigan interface --- src/scos_tekrsa/hardware/tekrsa_sigan.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/scos_tekrsa/hardware/tekrsa_sigan.py b/src/scos_tekrsa/hardware/tekrsa_sigan.py index cd82bce..ad8b4ed 100644 --- a/src/scos_tekrsa/hardware/tekrsa_sigan.py +++ b/src/scos_tekrsa/hardware/tekrsa_sigan.py @@ -8,7 +8,7 @@ ) import scos_tekrsa.hardware.tekrsa_constants as rsa_constants -from scos_tekrsa import __version__ as SCOS_TEKRSA_PLUGIN_VERSION +from scos_tekrsa import __version__ as SCOS_TEKRSA_VERSION from scos_tekrsa import settings from scos_tekrsa.hardware.mocks.rsa_block import MockRSA @@ -22,7 +22,7 @@ def __init__(self): try: super().__init__() logger.info("Initializing Tektronix RSA Signal Analyzer") - self.plugin_version = SCOS_TEKRSA_PLUGIN_VERSION + self._plugin_version = SCOS_TEKRSA_VERSION self.rsa = None self._is_available = False # should not be set outside of connect method @@ -108,6 +108,11 @@ def is_available(self): """Returns True if initialized and ready for measurements""" return self._is_available + @property + def plugin_version(self): + """Returns the current version of scos-tekrsa.""" + return self._plugin_version + @property def sample_rate(self): self._iq_bandwidth, self._sample_rate = self.rsa.IQSTREAM_GetAcqParameters() From 3c13365b8659c89d4d296966105b417a09f53cb3 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <aromaniello@ntia.gov> Date: Thu, 19 Oct 2023 12:06:34 -0400 Subject: [PATCH 09/13] Require scos-actions 6.4.0 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 393b551..548e78b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -41,7 +41,7 @@ classifiers = [ dependencies = [ "environs>=9.5.0", "tekrsa-api-wrap>=1.3.2", - "scos_actions @ git+https://github.com/NTIA/scos-actions@6.3.3", + "scos_actions @ git+https://github.com/NTIA/scos-actions@6.4.0", ] [project.optional-dependencies] From 1b2c38e12bdef175442006fbec05d34fe9d4f9c7 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <aromaniello@ntia.gov> Date: Thu, 19 Oct 2023 12:18:22 -0400 Subject: [PATCH 10/13] Switch to testing scos-actions branch --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 548e78b..65036f4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -41,7 +41,7 @@ classifiers = [ dependencies = [ "environs>=9.5.0", "tekrsa-api-wrap>=1.3.2", - "scos_actions @ git+https://github.com/NTIA/scos-actions@6.4.0", + "scos_actions @ git+https://github.com/NTIA/scos-actions@add-sea-action-cui-markings", ] [project.optional-dependencies] From 5927db220c183c0b9ddace197e874ed542a40687 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <aromaniello@ntia.gov> Date: Fri, 20 Oct 2023 12:32:25 -0400 Subject: [PATCH 11/13] tagged scos-actions 6.4.0 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 65036f4..548e78b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -41,7 +41,7 @@ classifiers = [ dependencies = [ "environs>=9.5.0", "tekrsa-api-wrap>=1.3.2", - "scos_actions @ git+https://github.com/NTIA/scos-actions@add-sea-action-cui-markings", + "scos_actions @ git+https://github.com/NTIA/scos-actions@6.4.0", ] [project.optional-dependencies] From 0167531ff7223f61e663ef685b7c4c5ac586eed9 Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <aromaniello@ntia.gov> Date: Fri, 20 Oct 2023 16:25:33 -0400 Subject: [PATCH 12/13] Switch scos actions version for testing --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 548e78b..6089140 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -41,7 +41,7 @@ classifiers = [ dependencies = [ "environs>=9.5.0", "tekrsa-api-wrap>=1.3.2", - "scos_actions @ git+https://github.com/NTIA/scos-actions@6.4.0", + "scos_actions @ git+https://github.com/NTIA/scos-actions@ntia-diagnostics-v1.1.2", ] [project.optional-dependencies] From f0654f9362e3cc8142e63445de566219905babda Mon Sep 17 00:00:00 2001 From: Anthony Romaniello <aromaniello@ntia.gov> Date: Mon, 23 Oct 2023 10:05:56 -0400 Subject: [PATCH 13/13] Update to tagged scos-actions 6.4.1 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 6089140..7ee9969 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -41,7 +41,7 @@ classifiers = [ dependencies = [ "environs>=9.5.0", "tekrsa-api-wrap>=1.3.2", - "scos_actions @ git+https://github.com/NTIA/scos-actions@ntia-diagnostics-v1.1.2", + "scos_actions @ git+https://github.com/NTIA/scos-actions@6.4.1", ] [project.optional-dependencies]