From f1e2041fbe17dc23fdef92f2dc0f918a725a60c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Holger=20M=C3=BCller?= Date: Wed, 7 Sep 2022 20:12:29 +0200 Subject: [PATCH 1/5] split complex method --- NanoVNASaver/Hardware/NanoVNA_V2.py | 35 ++++++++++++++++------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/NanoVNASaver/Hardware/NanoVNA_V2.py b/NanoVNASaver/Hardware/NanoVNA_V2.py index d067564..d632814 100644 --- a/NanoVNASaver/Hardware/NanoVNA_V2.py +++ b/NanoVNASaver/Hardware/NanoVNA_V2.py @@ -130,6 +130,22 @@ class NanoVNA_V2(VNA): int(self.sweepStartHz + i * self.sweepStepHz) for i in range(self.datapoints)] + def _read_pointstoread(self, pointstoread, arr) -> None: + freq_index = -1 + + for i in range(pointstoread): + (fwd_real, fwd_imag, rev0_real, rev0_imag, rev1_real, + rev1_imag, freq_index) = unpack_from( + " List[str]: # Actually grab the data only when requesting channel 0. # The hardware will return all channels which we will store. @@ -146,7 +162,7 @@ class NanoVNA_V2(VNA): sleep(WRITE_SLEEP) # clear sweepdata self._sweepdata = [(complex(), complex())] * ( - self.datapoints + s21hack) + self.datapoints + s21hack) pointstodo = self.datapoints + s21hack # we read at most 255 values at a time and the time required empirically is # just over 3 seconds for 101 points or 7 seconds for 255 points @@ -175,18 +191,7 @@ class NanoVNA_V2(VNA): if nBytes != len(arr): return [] - freq_index = -1 - for i in range(pointstoread): - (fwd_real, fwd_imag, rev0_real, rev0_imag, rev1_real, - rev1_imag, freq_index) = unpack_from( - " Date: Wed, 7 Sep 2022 20:13:09 +0200 Subject: [PATCH 2/5] raise IOError if read_board_revision fails --- NanoVNASaver/Hardware/NanoVNA_V2.py | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/NanoVNASaver/Hardware/NanoVNA_V2.py b/NanoVNASaver/Hardware/NanoVNA_V2.py index d632814..f6d411a 100644 --- a/NanoVNASaver/Hardware/NanoVNA_V2.py +++ b/NanoVNASaver/Hardware/NanoVNA_V2.py @@ -213,10 +213,8 @@ class NanoVNA_V2(VNA): def resetSweep(self, start: int, stop: int): self.setSweep(start, stop) - def readVersion(self) -> 'Version': - cmd = pack(" 'Version': + result = self._read_version(_ADDR_FW_MAJOR, + _ADDR_FW_MINOR) logger.debug("readVersion: %s", result) return result def read_board_revision(self) -> 'Version': - cmd = pack(" Date: Wed, 7 Sep 2022 20:26:58 +0200 Subject: [PATCH 3/5] Linting and simplifying readValues in NanoVNA_V2 --- NanoVNASaver/Hardware/NanoVNA_V2.py | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/NanoVNASaver/Hardware/NanoVNA_V2.py b/NanoVNASaver/Hardware/NanoVNA_V2.py index f6d411a..c08eb36 100644 --- a/NanoVNASaver/Hardware/NanoVNA_V2.py +++ b/NanoVNASaver/Hardware/NanoVNA_V2.py @@ -128,7 +128,8 @@ class NanoVNA_V2(VNA): def readFrequencies(self) -> List[int]: return [ int(self.sweepStartHz + i * self.sweepStepHz) - for i in range(self.datapoints)] + for i in range(self.datapoints) + ] def _read_pointstoread(self, pointstoread, arr) -> None: freq_index = -1 @@ -150,7 +151,7 @@ class NanoVNA_V2(VNA): # Actually grab the data only when requesting channel 0. # The hardware will return all channels which we will store. if value == "data 0": - s21hack = "S21 hack" in self.features + s21hack = 1 if "S21 hack" in self.features else 0 # reset protocol to known state timeout = self.serial.timeout with self.serial.lock: @@ -199,16 +200,11 @@ class NanoVNA_V2(VNA): if s21hack: self._sweepdata = self._sweepdata[1:] - ret = [x[0] for x in self._sweepdata] - ret = [f'{str(x.real)} {str(x.imag)}' for x in ret] - return ret - - if value == "data 1": - ret = [x[1] for x in self._sweepdata] - ret = [f'{str(x.real)} {str(x.imag)}' for x in ret] - return ret - - return [] + idx = 1 if value == "data 1" else 0 + return [ + f'{str(x[idx].real)} {str(x[idx].imag)}' + for x in self._sweepdata + ] def resetSweep(self, start: int, stop: int): self.setSweep(start, stop) From 8814e4f4718d93895a03b89731db4a8d0fddf496 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Holger=20M=C3=BCller?= Date: Wed, 7 Sep 2022 21:12:54 +0200 Subject: [PATCH 4/5] use numpy array for TDRWindow.td attribute --- NanoVNASaver/Charts/TDR.py | 67 +++++++++++++++++++---------------- NanoVNASaver/Windows/TDR.py | 70 ++++++++++++++++++++++--------------- 2 files changed, 79 insertions(+), 58 deletions(-) diff --git a/NanoVNASaver/Charts/TDR.py b/NanoVNASaver/Charts/TDR.py index 16d80bb..3e2fd22 100644 --- a/NanoVNASaver/Charts/TDR.py +++ b/NanoVNASaver/Charts/TDR.py @@ -109,11 +109,13 @@ class TDRChart(Chart): self.y_action_set_fixed_maximum = QtWidgets.QAction( f"Maximum ({self.maxImpedance})") - self.y_action_set_fixed_maximum.triggered.connect(self.setMaximumImpedance) + self.y_action_set_fixed_maximum.triggered.connect( + self.setMaximumImpedance) self.y_action_set_fixed_minimum = QtWidgets.QAction( f"Minimum ({self.minImpedance})") - self.y_action_set_fixed_minimum.triggered.connect(self.setMinimumImpedance) + self.y_action_set_fixed_minimum.triggered.connect( + self.setMinimumImpedance) self.y_menu.addAction(self.y_action_set_fixed_maximum) self.y_menu.addAction(self.y_action_set_fixed_minimum) @@ -143,7 +145,7 @@ class TDRChart(Chart): def isPlotable(self, x, y): return self.leftMargin <= x <= self.width() - self.rightMargin and \ - self.topMargin <= y <= self.height() - self.bottomMargin + self.topMargin <= y <= self.height() - self.bottomMargin def resetDisplayLimits(self): self.fixedSpan = False @@ -256,10 +258,12 @@ class TDRChart(Chart): return a0.accept() width = self.width() - self.leftMargin - self.rightMargin - if len(self.tdrWindow.td) > 0: + if self.tdrWindow.td.size: if self.fixedSpan: - max_index = np.searchsorted(self.tdrWindow.distance_axis, self.maxDisplayLength * 2) - min_index = np.searchsorted(self.tdrWindow.distance_axis, self.minDisplayLength * 2) + max_index = np.searchsorted( + self.tdrWindow.distance_axis, self.maxDisplayLength * 2) + min_index = np.searchsorted( + self.tdrWindow.distance_axis, self.minDisplayLength * 2) x_step = (max_index - min_index) / width else: max_index = math.ceil(len(self.tdrWindow.distance_axis) / 2) @@ -290,11 +294,13 @@ class TDRChart(Chart): ticks = math.floor((self.width() - self.leftMargin) / 100) self.drawTitle(qp) - if self.tdrWindow.td: + if self.tdrWindow.td.size: if self.fixedSpan: max_length = max(0.1, self.maxDisplayLength) - max_index = np.searchsorted(self.tdrWindow.distance_axis, max_length * 2) - min_index = np.searchsorted(self.tdrWindow.distance_axis, self.minDisplayLength * 2) + max_index = np.searchsorted( + self.tdrWindow.distance_axis, max_length * 2) + min_index = np.searchsorted( + self.tdrWindow.distance_axis, self.minDisplayLength * 2) if max_index == min_index: if max_index < len(self.tdrWindow.distance_axis) - 1: max_index += 1 @@ -343,7 +349,7 @@ class TDRChart(Chart): 1)) + "m") y_ticks = math.floor(height / 60) - y_tick_step = height/y_ticks + y_tick_step = height / y_ticks for i in range(y_ticks): y = self.bottomMargin + int(i * y_tick_step) @@ -353,7 +359,8 @@ class TDRChart(Chart): qp.setPen(Chart.color.text) qp.drawText(3, y + 3, str(round(y_val, 1))) - qp.drawText(3, self.topMargin + height + 3, str(round(min_impedance, 1))) + qp.drawText(3, self.topMargin + height + 3, + str(round(min_impedance, 1))) pen = QtGui.QPen(Chart.color.sweep) pen.setWidth(self.dim.point) @@ -363,15 +370,17 @@ class TDRChart(Chart): continue x = self.leftMargin + int((i - min_index) / x_step) - y = (self.topMargin + height) - int(self.tdrWindow.td[i] / y_step) + y = (self.topMargin + height) - \ + int(self.tdrWindow.td[i] / y_step) if self.isPlotable(x, y): pen.setColor(Chart.color.sweep) qp.setPen(pen) qp.drawPoint(x, y) x = self.leftMargin + int((i - min_index) / x_step) - y = (self.topMargin + height) -\ - int((self.tdrWindow.step_response_Z[i]-min_impedance) / y_impedance_step) + y = (self.topMargin + height) - int( + (self.tdrWindow.step_response_Z[i] - min_impedance) / + y_impedance_step) if self.isPlotable(x, y): pen.setColor(Chart.color.sweep_secondary) qp.setPen(pen) @@ -403,7 +412,8 @@ class TDRChart(Chart): 2)) + "m") if self.dragbox.state and self.dragbox.pos[0] != -1: - dashed_pen = QtGui.QPen(Chart.color.foreground, 1, QtCore.Qt.DashLine) + dashed_pen = QtGui.QPen( + Chart.color.foreground, 1, QtCore.Qt.DashLine) qp.setPen(dashed_pen) qp.drawRect( QtCore.QRect( @@ -415,7 +425,7 @@ class TDRChart(Chart): qp.end() def valueAtPosition(self, y): - if len(self.tdrWindow.td) > 0: + if self.tdrWindow.td.size: height = self.height() - self.topMargin - self.bottomMargin absy = (self.height() - y) - self.bottomMargin if self.fixedValues: @@ -433,27 +443,24 @@ class TDRChart(Chart): return 0 def lengthAtPosition(self, x, limit=True): - if not self.tdrWindow.td: + if not self.tdrWindow.td.size: return 0 width = self.width() - self.leftMargin - self.rightMargin absx = x - self.leftMargin - if self.fixedSpan: - max_length = self.maxDisplayLength - min_length = self.minDisplayLength - x_step = (max_length - min_length) / width - else: - min_length = 0 - max_length = self.tdrWindow.distance_axis[ - math.ceil(len(self.tdrWindow.distance_axis) / 2)] / 2 - x_step = max_length / width + min_length = self.minDisplayLength if self.fixedSpan else 0 + max_length = self.maxDisplayLength if self.fixedSpan else ( + self.tdrWindow.distance_axis[ + math.ceil(len(self.tdrWindow.distance_axis) / 2) + ] / 2) + + x_step = (max_length - min_length) / width if limit and absx < 0: return min_length - if limit and absx > width: - return max_length - return absx * x_step + min_length + return max_length if limit and absx > width else absx * x_step + min_length def zoomTo(self, x1, y1, x2, y2): - logger.debug("Zoom to (x,y) by (x,y): (%d, %d) by (%d, %d)", x1, y1, x2, y2) + logger.debug( + "Zoom to (x,y) by (x,y): (%d, %d) by (%d, %d)", x1, y1, x2, y2) val1 = self.valueAtPosition(y1) val2 = self.valueAtPosition(y2) diff --git a/NanoVNASaver/Windows/TDR.py b/NanoVNASaver/Windows/TDR.py index 7006dbe..1fcda6c 100644 --- a/NanoVNASaver/Windows/TDR.py +++ b/NanoVNASaver/Windows/TDR.py @@ -20,9 +20,9 @@ import logging import math import numpy as np -import scipy.signal as signal -from PyQt5 import QtWidgets, QtCore +from scipy import signal +from PyQt5 import QtWidgets, QtCore logger = logging.getLogger(__name__) @@ -34,7 +34,7 @@ class TDRWindow(QtWidgets.QWidget): super().__init__() self.app = app - self.td = [] + self.td = np.array([]) self.distance_axis = [] self.step_response = [] self.step_response_Z = [] @@ -54,33 +54,47 @@ class TDRWindow(QtWidgets.QWidget): self.tdr_velocity_dropdown.addItem("Pulp Insulation (0.72)", 0.72) self.tdr_velocity_dropdown.addItem("Foam or Cellular PE (0.78)", 0.78) self.tdr_velocity_dropdown.addItem("Semi-solid PE (SSPE) (0.84)", 0.84) - self.tdr_velocity_dropdown.addItem("Air (Helical spacers) (0.94)", 0.94) - self.tdr_velocity_dropdown.insertSeparator(self.tdr_velocity_dropdown.count()) + self.tdr_velocity_dropdown.addItem( + "Air (Helical spacers) (0.94)", 0.94) + self.tdr_velocity_dropdown.insertSeparator( + self.tdr_velocity_dropdown.count()) # Lots of cable types added by Larry Goga, AE5CZ - self.tdr_velocity_dropdown.addItem("RG-6/U PE 75\N{OHM SIGN} (Belden 8215) (0.66)", 0.66) - self.tdr_velocity_dropdown.addItem("RG-6/U Foam 75\N{OHM SIGN} (Belden 9290) (0.81)", 0.81) - self.tdr_velocity_dropdown.addItem("RG-8/U PE 50\N{OHM SIGN} (Belden 8237) (0.66)", 0.66) - self.tdr_velocity_dropdown.addItem("RG-8/U Foam (Belden 8214) (0.78)", 0.78) + self.tdr_velocity_dropdown.addItem( + "RG-6/U PE 75\N{OHM SIGN} (Belden 8215) (0.66)", 0.66) + self.tdr_velocity_dropdown.addItem( + "RG-6/U Foam 75\N{OHM SIGN} (Belden 9290) (0.81)", 0.81) + self.tdr_velocity_dropdown.addItem( + "RG-8/U PE 50\N{OHM SIGN} (Belden 8237) (0.66)", 0.66) + self.tdr_velocity_dropdown.addItem( + "RG-8/U Foam (Belden 8214) (0.78)", 0.78) self.tdr_velocity_dropdown.addItem("RG-8/U (Belden 9913) (0.84)", 0.84) # Next one added by EKZ, KC3KZ, from measurement of actual cable - self.tdr_velocity_dropdown.addItem("RG-8/U (Shireen RFC®400 Low Loss) (0.86)", 0.86) + self.tdr_velocity_dropdown.addItem( + "RG-8/U (Shireen RFC®400 Low Loss) (0.86)", 0.86) self.tdr_velocity_dropdown.addItem("RG-8X (Belden 9258) (0.82)", 0.82) # Next three added by EKZ, KC3KZ, from measurement of actual cable - self.tdr_velocity_dropdown.addItem("RG-8X (Wireman \"Super 8\" CQ106) (0.81)", 0.81) - self.tdr_velocity_dropdown.addItem("RG-8X (Wireman \"MINI-8 Lo-Loss\" CQ118) (0.82)", 0.82) + self.tdr_velocity_dropdown.addItem( + "RG-8X (Wireman \"Super 8\" CQ106) (0.81)", 0.81) + self.tdr_velocity_dropdown.addItem( + "RG-8X (Wireman \"MINI-8 Lo-Loss\" CQ118) (0.82)", 0.82) self.tdr_velocity_dropdown.addItem( "RG-58 (Wireman \"CQ 58 Lo-Loss Flex\" CQ129FF) (0.79)", 0.79) self.tdr_velocity_dropdown.addItem( "RG-11/U 75\N{OHM SIGN} Foam HDPE (Belden 9292) (0.84)", 0.84) - self.tdr_velocity_dropdown.addItem("RG-58/U 52\N{OHM SIGN} PE (Belden 9201) (0.66)", 0.66) + self.tdr_velocity_dropdown.addItem( + "RG-58/U 52\N{OHM SIGN} PE (Belden 9201) (0.66)", 0.66) self.tdr_velocity_dropdown.addItem( "RG-58A/U 54\N{OHM SIGN} Foam (Belden 8219) (0.73)", 0.73) - self.tdr_velocity_dropdown.addItem("RG-59A/U PE 75\N{OHM SIGN} (Belden 8241) (0.66)", 0.66) + self.tdr_velocity_dropdown.addItem( + "RG-59A/U PE 75\N{OHM SIGN} (Belden 8241) (0.66)", 0.66) self.tdr_velocity_dropdown.addItem( "RG-59A/U Foam 75\N{OHM SIGN} (Belden 8241F) (0.78)", 0.78) - self.tdr_velocity_dropdown.addItem("RG-174 PE (Belden 8216)(0.66)", 0.66) - self.tdr_velocity_dropdown.addItem("RG-174 Foam (Belden 7805R) (0.735)", 0.735) - self.tdr_velocity_dropdown.addItem("RG-213/U PE (Belden 8267) (0.66)", 0.66) + self.tdr_velocity_dropdown.addItem( + "RG-174 PE (Belden 8216)(0.66)", 0.66) + self.tdr_velocity_dropdown.addItem( + "RG-174 Foam (Belden 7805R) (0.735)", 0.735) + self.tdr_velocity_dropdown.addItem( + "RG-213/U PE (Belden 8267) (0.66)", 0.66) self.tdr_velocity_dropdown.addItem("RG316 (0.695)", 0.695) self.tdr_velocity_dropdown.addItem("RG402 (0.695)", 0.695) self.tdr_velocity_dropdown.addItem("LMR-240 (0.84)", 0.84) @@ -88,7 +102,8 @@ class TDRWindow(QtWidgets.QWidget): self.tdr_velocity_dropdown.addItem("LMR-400 (0.85)", 0.85) self.tdr_velocity_dropdown.addItem("LMR400UF (0.83)", 0.83) self.tdr_velocity_dropdown.addItem("Davis Bury-FLEX (0.82)", 0.82) - self.tdr_velocity_dropdown.insertSeparator(self.tdr_velocity_dropdown.count()) + self.tdr_velocity_dropdown.insertSeparator( + self.tdr_velocity_dropdown.count()) self.tdr_velocity_dropdown.addItem("Custom", -1) self.tdr_velocity_dropdown.setCurrentIndex(1) # Default to PE (0.66) @@ -121,7 +136,8 @@ class TDRWindow(QtWidgets.QWidget): self.tdr_velocity_input.setDisabled(False) else: self.tdr_velocity_input.setDisabled(True) - self.tdr_velocity_input.setText(str(self.tdr_velocity_dropdown.currentData())) + self.tdr_velocity_input.setText( + str(self.tdr_velocity_dropdown.currentData())) try: v = float(self.tdr_velocity_input.text()) @@ -134,10 +150,7 @@ class TDRWindow(QtWidgets.QWidget): logger.info("Cannot compute cable length at 0 span") return - s11 = [] - for d in self.app.data.s11: - s11.append(np.complex(d.re, d.im)) - + s11 = [np.complex(d.re, d.im) for d in self.app.data.s11] window = np.blackman(len(self.app.data.s11)) windowed_s11 = window * s11 @@ -145,18 +158,19 @@ class TDRWindow(QtWidgets.QWidget): step = np.ones(FFT_POINTS) self.step_response = signal.convolve(self.td, step) - self.step_response_Z = 50 * (1 + self.step_response) / (1 - self.step_response) + self.step_response_Z = 50 * ( + 1 + self.step_response) / (1 - self.step_response) - time_axis = np.linspace(0, 1/step_size, FFT_POINTS) + time_axis = np.linspace(0, 1 / step_size, FFT_POINTS) self.distance_axis = time_axis * v * c # peak = np.max(td) # We should check that this is an actual *peak*, and not just a vague maximum index_peak = np.argmax(self.td) - cable_len = round(self.distance_axis[index_peak]/2, 3) + cable_len = round(self.distance_axis[index_peak] / 2, 3) feet = math.floor(cable_len / 0.3048) - inches = round(((cable_len / 0.3048) - feet)*12, 1) + inches = round(((cable_len / 0.3048) - feet) * 12, 1) self.tdr_result_label.setText(f"{cable_len}m ({feet}ft {inches}in)") - self.app.tdr_result_label.setText(str(cable_len) + " m") + self.app.tdr_result_label.setText(f"{cable_len}m") self.updated.emit() From b3f816e5f5e56f5c8617c89fb57dab87a0fe1569 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Holger=20M=C3=BCller?= Date: Thu, 8 Sep 2022 08:52:31 +0200 Subject: [PATCH 5/5] updated libraries for binary builds --- .github/workflows/release_linux.yml | 4 ++-- .github/workflows/release_macos.yml | 4 ++-- .github/workflows/release_win.yml | 4 ++-- requirements.txt | 6 +++--- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/release_linux.yml b/.github/workflows/release_linux.yml index 91f4d49..b5b1d46 100644 --- a/.github/workflows/release_linux.yml +++ b/.github/workflows/release_linux.yml @@ -23,9 +23,9 @@ jobs: run: | python3.9 -m venv build . build/bin/activate - python -m pip install pip==22.1.2 setuptools==62.6.0 + python -m pip install pip==22.2.2 setuptools==65.3.0 pip install -r requirements.txt - pip install PyInstaller==5.1 + pip install PyInstaller==5.3 - name: Build binary run: | . build/bin/activate diff --git a/.github/workflows/release_macos.yml b/.github/workflows/release_macos.yml index ba11d4f..4cefcd9 100644 --- a/.github/workflows/release_macos.yml +++ b/.github/workflows/release_macos.yml @@ -18,9 +18,9 @@ jobs: python-version: 3.9 - name: Install dependencies and pyinstall run: | - python -m pip install pip==22.1.2 setuptools==62.6.0 + python -m pip install pip==22.2.2 setuptools==65.3.0 pip install -r requirements.txt - pip install PyInstaller=5.1 + pip install PyInstaller=5.3 - name: Build binary run: | pyinstaller --onefile -n nanovna-saver nanovna-saver.py diff --git a/.github/workflows/release_win.yml b/.github/workflows/release_win.yml index 8dcfacb..cd87834 100644 --- a/.github/workflows/release_win.yml +++ b/.github/workflows/release_win.yml @@ -22,9 +22,9 @@ jobs: architecture: ${{ matrix.arch }} - name: Install dependencies and pyinstall run: | - python -m pip install pip==22.1.2 setuptools==62.6.0 + python -m pip install pip==22.2.2 setuptools==65.3.0 pip install -r requirements.txt - pip install PyInstaller==5.1 + pip install PyInstaller==5.3 - name: Build binary run: | pyinstaller --onefile -n nanovna-saver.exe nanovna-saver.py diff --git a/requirements.txt b/requirements.txt index ed6c7d6..13e77d3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ pyserial==3.5 PyQt5==5.15.7 -numpy==1.23.0 -scipy==1.8.1 -Cython==0.29.30 +numpy==1.23.2 +scipy==1.9.1 +Cython==0.29.32