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]