From 8df00191bb3198f96fcd6d4aa71e9603d0e6d45b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Holger=20M=C3=BCller?= Date: Tue, 10 Dec 2024 19:30:15 +0100 Subject: [PATCH] fix crash when truncating averages (#743) --- .github/workflows/release_linux.yml | 4 +- src/NanoVNASaver/SweepWorker.py | 23 +++++----- tests/test_truncate.py | 70 +++++++++++++++++++++++++++++ 3 files changed, 85 insertions(+), 12 deletions(-) create mode 100644 tests/test_truncate.py diff --git a/.github/workflows/release_linux.yml b/.github/workflows/release_linux.yml index 16e0a733..2220f729 100644 --- a/.github/workflows/release_linux.yml +++ b/.github/workflows/release_linux.yml @@ -1,4 +1,4 @@ -name: Modern Linux Release +name: Linux Release on: push: @@ -44,5 +44,5 @@ jobs: - name: Archive production artifacts uses: actions/upload-artifact@v4 with: - name: NanoVNASaver.linux_modern + name: NanoVNASaver.linux path: dist/nanovna-saver diff --git a/src/NanoVNASaver/SweepWorker.py b/src/NanoVNASaver/SweepWorker.py index da2ed536..01d76be1 100644 --- a/src/NanoVNASaver/SweepWorker.py +++ b/src/NanoVNASaver/SweepWorker.py @@ -39,7 +39,7 @@ RETRIES_MAX: int = 10 -def truncate(values: list[list[tuple]], count: int) -> list[list[tuple]]: +def truncate(values: list[list[complex]], count: int) -> list[list[complex]]: """truncate drops extrema from data list if averaging is active""" keep = len(values) - count logger.debug("Truncating from %d values to %d", len(values), keep) @@ -48,9 +48,9 @@ def truncate(values: list[list[tuple]], count: int) -> list[list[tuple]]: return values truncated = [] for valueset in np.swapaxes(values, 0, 1).tolist(): - avg = complex(*np.average(valueset, 0)) + avg = np.average(valueset) truncated.append( - sorted(valueset, key=lambda v, a=avg: abs(a - complex(*v)))[:keep] + sorted(valueset, key=lambda v, a=avg: abs(a - v))[:keep] ) return np.swapaxes(truncated, 0, 1).tolist() @@ -82,7 +82,7 @@ def __init__(self, app: "NanoVNA") -> None: self.init_data() self.state: "SweepState" = SweepState.STOPPED self.error_message: str = "" - self.offsetDelay = 0 + self.offsetDelay: float = 0.0 @pyqtSlot() def run(self) -> None: @@ -229,7 +229,7 @@ def applyCalibration( else: data21 = raw_data21 - if self.offsetDelay != 0: + if self.offsetDelay != 0.0: data11 = [ correct_delay(dp, self.offsetDelay, reflect=True) for dp in data11 @@ -238,7 +238,9 @@ def applyCalibration( return data11, data21 - def read_averaged_segment(self, start: int, stop: int, averages: int = 1): + def read_averaged_segment( + self, start: int, stop: int, averages: int = 1 + ) -> tuple[list[int], list[complex], list[complex]]: logger.info( "Reading from %d to %d. Averaging %d values", start, stop, averages ) @@ -261,11 +263,13 @@ def read_averaged_segment(self, start: int, stop: int, averages: int = 1): while retries and not tmp_11: if retries < RETRIES_RECONNECT: logger.warning("retry readSegment(%s,%s)", start, stop) + sleep(0.5) retries -= 1 freq, tmp_11, tmp_21 = self.read_segment(start, stop) - sleep(0.5) + if not tmp_11: raise IOError("Invalid data during swwep") + values11.append(tmp_11) values21.append(tmp_21) self.percentage += 100 / (self.sweep.segments * averages) @@ -281,9 +285,8 @@ def read_averaged_segment(self, start: int, stop: int, averages: int = 1): values21 = truncate(values21, truncates) logger.debug("Averaging %d values", len(values11)) - values11 = np.average(values11, 0).tolist() - values21 = np.average(values21, 0).tolist() - + values11: list[complex] = np.average(values11, axis=0).tolist() + values21: list[complex] = np.average(values21, axis=0).tolist() return freq, values11, values21 def read_segment( diff --git a/tests/test_truncate.py b/tests/test_truncate.py new file mode 100644 index 00000000..5097d383 --- /dev/null +++ b/tests/test_truncate.py @@ -0,0 +1,70 @@ +# NanoVNASaver +# +# A python program to view and export Touchstone data from a NanoVNA +# Copyright (C) 2019, 2020 Rune B. Broberg +# Copyright (C) 2020,2021 NanoVNA-Saver Authors +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +import unittest + +# Import targets to be tested +from NanoVNASaver.SweepWorker import truncate + +DATA = [ + [ + (-0.81474496 + 0.639054208j), + (-0.809070272 + 0.645022336j), + (-0.80785184 + 0.646469184j), + ], + [ + (-0.81436032 + 0.638994432j), + (-0.809495232 + 0.645203008j), + (-0.808114176 + 0.646456512j), + ], + [ + (-0.814578048 + 0.638436288j), + (-0.809082496 + 0.644978368j), + (-0.807828096 + 0.646324352j), + ], + [ + (-0.814171712 + 0.639012992j), + (-0.80954272 + 0.645197312j), + (-0.807910976 + 0.646379968j), + ], +] + +DATA_TRUNCATED = [ + [ + (-0.81436032 + 0.638994432j), + (-0.809495232 + 0.645203008j), + (-0.807910976 + 0.646379968j), + ], + [ + (-0.814171712 + 0.639012992j), + (-0.809070272 + 0.645022336j), + (-0.80785184 + 0.646469184j), + ], + [ + (-0.81474496 + 0.639054208j), + (-0.809082496 + 0.644978368j), + (-0.807828096 + 0.646324352j), + ], +] + + +class TestSweepWorkerTruncate(unittest.TestCase): + + def test_truncate(self): + x = truncate(DATA, 1) + self.assertEqual(x, DATA_TRUNCATED)