kopia lustrzana https://github.com/NanoVNA-Saver/nanovna-saver
- TDR window with a chart of the TDR response
rodzic
eafb042bff
commit
a5a4e0385a
|
@ -16,6 +16,7 @@
|
|||
import collections
|
||||
import math
|
||||
from typing import List
|
||||
import numpy as np
|
||||
|
||||
from PyQt5 import QtWidgets, QtGui, QtCore
|
||||
|
||||
|
@ -1103,3 +1104,58 @@ class QualityFactorChart(Chart):
|
|||
re = d.re
|
||||
im = d.im
|
||||
return -math.degrees(math.atan2(im, re))
|
||||
|
||||
|
||||
class TDRChart(Chart):
|
||||
def __init__(self, name):
|
||||
super().__init__()
|
||||
self.name = name
|
||||
self.tdrWindow = None
|
||||
self.leftMargin = 20
|
||||
self.rightMargin = 20
|
||||
self.lowerMargin = 35
|
||||
self.setMinimumSize(400, 400)
|
||||
self.setSizePolicy(QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding))
|
||||
pal = QtGui.QPalette()
|
||||
pal.setColor(QtGui.QPalette.Background, self.backgroundColor)
|
||||
self.setPalette(pal)
|
||||
self.setAutoFillBackground(True)
|
||||
|
||||
def paintEvent(self, a0: QtGui.QPaintEvent) -> None:
|
||||
qp = QtGui.QPainter(self)
|
||||
qp.setPen(QtGui.QPen(self.textColor))
|
||||
qp.drawText(3, 15, self.name)
|
||||
|
||||
width = self.width() - self.leftMargin - self.rightMargin
|
||||
height = self.height() - self.lowerMargin
|
||||
|
||||
qp.setPen(QtCore.Qt.lightGray)
|
||||
qp.drawLine(self.leftMargin - 5, self.height() - self.lowerMargin, self.width() - self.rightMargin,
|
||||
self.height() - self.lowerMargin)
|
||||
qp.drawLine(self.leftMargin, 20, self.leftMargin, self.height() - self.lowerMargin + 5)
|
||||
|
||||
ticks = math.floor((self.width() - self.leftMargin)/100) # Number of ticks does not include the origin
|
||||
|
||||
if len(self.tdrWindow.td) > 0:
|
||||
x_step = len(self.tdrWindow.distance_axis) / width
|
||||
y_step = np.max(self.tdrWindow.td)*1.1 / height
|
||||
|
||||
for i in range(ticks):
|
||||
x = self.leftMargin + round((i + 1) * width / ticks)
|
||||
qp.setPen(QtGui.QPen(QtGui.QColor("lightgray")))
|
||||
qp.drawLine(x, 20, x, height)
|
||||
qp.setPen(QtGui.QPen(self.textColor))
|
||||
qp.drawText(x - 20, 20 + height,
|
||||
str(round(self.tdrWindow.distance_axis[int((x - self.leftMargin) * x_step) - 1]/2, 1)) + "m")
|
||||
|
||||
qp.setPen(self.tdrWindow.app.sweepColor)
|
||||
for i in range(len(self.tdrWindow.distance_axis)):
|
||||
qp.drawPoint(self.leftMargin + int(i / x_step), height - int(self.tdrWindow.td[i] / y_step))
|
||||
id_max = np.argmax(self.tdrWindow.td)
|
||||
max_point = QtCore.QPoint(self.leftMargin + int(id_max / x_step),
|
||||
height - int(self.tdrWindow.td[id_max] / y_step))
|
||||
qp.setPen(self.markers[0].color)
|
||||
qp.drawEllipse(max_point, 2, 2)
|
||||
qp.setPen(self.textColor)
|
||||
qp.drawText(max_point.x() - 10, max_point.y() - 5, str(round(self.tdrWindow.distance_axis[id_max]/2, 2)) + "m")
|
||||
qp.end()
|
||||
|
|
|
@ -24,7 +24,7 @@ import serial
|
|||
from PyQt5 import QtWidgets, QtCore, QtGui
|
||||
from serial.tools import list_ports
|
||||
|
||||
from .Chart import Chart, PhaseChart, VSWRChart, PolarChart, SmithChart, LogMagChart, QualityFactorChart
|
||||
from .Chart import Chart, PhaseChart, VSWRChart, PolarChart, SmithChart, LogMagChart, QualityFactorChart, TDRChart
|
||||
from .Calibration import CalibrationWindow, Calibration
|
||||
from .Marker import Marker
|
||||
from .SweepWorker import SweepWorker
|
||||
|
@ -114,6 +114,9 @@ class NanoVNASaver(QtWidgets.QWidget):
|
|||
|
||||
self.charts = self.s11charts + self.s21charts
|
||||
|
||||
self.tdr_chart = TDRChart("TDR")
|
||||
self.charts.append(self.tdr_chart)
|
||||
|
||||
self.charts_layout = QtWidgets.QGridLayout()
|
||||
|
||||
left_column = QtWidgets.QVBoxLayout()
|
||||
|
@ -299,62 +302,23 @@ class NanoVNASaver(QtWidgets.QWidget):
|
|||
# TDR
|
||||
################################################################################################################
|
||||
|
||||
self.tdr_window = TDRWindow(self)
|
||||
self.tdr_chart.tdrWindow = self.tdr_window
|
||||
|
||||
tdr_control_box = QtWidgets.QGroupBox()
|
||||
tdr_control_box.setTitle("TDR")
|
||||
tdr_control_layout = QtWidgets.QFormLayout()
|
||||
tdr_control_box.setLayout(tdr_control_layout)
|
||||
tdr_control_box.setMaximumWidth(250)
|
||||
|
||||
self.tdr_velocity_dropdown = QtWidgets.QComboBox()
|
||||
self.tdr_velocity_dropdown.addItem("Jelly filled (0.64)", 0.64)
|
||||
self.tdr_velocity_dropdown.addItem("Polyethylene (0.66)", 0.66)
|
||||
self.tdr_velocity_dropdown.addItem("PTFE (Teflon) (0.70)", 0.70)
|
||||
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())
|
||||
# Lots of table types added by Larry Goga, AE5CZ
|
||||
self.tdr_velocity_dropdown.addItem("RG-6/U PE 75ohm (Belden 8215) (0.66)", 0.66)
|
||||
self.tdr_velocity_dropdown.addItem("RG-6/U Foam 75 ohm (Belden 9290) (0.81)", 0.81)
|
||||
self.tdr_velocity_dropdown.addItem("RG-8/U PE 75 ohm (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)
|
||||
self.tdr_velocity_dropdown.addItem("RG-8X (Belden9258) (0.82)", 0.82)
|
||||
self.tdr_velocity_dropdown.addItem("RG-11/U 75 ohm Foam HDPE (Belden 9292) (0.84)", 0.84)
|
||||
self.tdr_velocity_dropdown.addItem("RG-58/U PE (Belden 9201) (0.66)", 0.66)
|
||||
self.tdr_velocity_dropdown.addItem("RG-58A/U Foam (Belden 8219) (0.73)", 0.73)
|
||||
self.tdr_velocity_dropdown.addItem("RG-59A/U PE 75 ohm (Belden 8241) (0.66)", 0.66)
|
||||
self.tdr_velocity_dropdown.addItem("RG-59A/U Foam 75 ohm (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("RG316 (0.69)", 0.69)
|
||||
self.tdr_velocity_dropdown.addItem("RG402 (0.695)", 0.695)
|
||||
self.tdr_velocity_dropdown.addItem("LMR-240 (0.84)", 0.84)
|
||||
self.tdr_velocity_dropdown.addItem("LMR-240UF (0.80)", 0.80)
|
||||
self.tdr_velocity_dropdown.addItem("LMR-400 (0.85)", 0.85)
|
||||
self.tdr_velocity_dropdown.addItem("LMR400UF (0.85)", 0.85)
|
||||
self.tdr_velocity_dropdown.addItem("Davis BuryFlex (0.82)", 0.82)
|
||||
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)
|
||||
|
||||
self.tdr_velocity_dropdown.currentIndexChanged.connect(self.updateTDR)
|
||||
|
||||
tdr_control_layout.addRow(self.tdr_velocity_dropdown)
|
||||
|
||||
self.tdr_velocity_input = QtWidgets.QLineEdit()
|
||||
self.tdr_velocity_input.setDisabled(True)
|
||||
self.tdr_velocity_input.setText("0.66")
|
||||
self.tdr_velocity_input.textChanged.connect(self.updateTDR)
|
||||
|
||||
tdr_control_layout.addRow("Velocity factor", self.tdr_velocity_input)
|
||||
|
||||
self.tdr_result_label = QtWidgets.QLabel()
|
||||
tdr_control_layout.addRow("Estimated cable length:", self.tdr_result_label)
|
||||
|
||||
self.tdr_button = QtWidgets.QPushButton("Time Domain Reflectometry ...")
|
||||
self.tdr_button.clicked.connect(self.displayTDRWindow)
|
||||
|
||||
tdr_control_layout.addRow(self.tdr_button)
|
||||
|
||||
left_column.addWidget(tdr_control_box)
|
||||
|
||||
################################################################################################################
|
||||
|
@ -745,8 +709,10 @@ class NanoVNASaver(QtWidgets.QWidget):
|
|||
|
||||
for c in self.s21charts:
|
||||
c.setData(self.data21)
|
||||
|
||||
self.sweepProgressBar.setValue(self.worker.percentage)
|
||||
self.updateTDR()
|
||||
self.tdr_window.updateTDR()
|
||||
|
||||
# Find the minimum S11 VSWR:
|
||||
minVSWR = 100
|
||||
minVSWRfreq = -1
|
||||
|
@ -842,46 +808,6 @@ class NanoVNASaver(QtWidgets.QWidget):
|
|||
self.btnSweep.setDisabled(False)
|
||||
self.btnStopSweep.setDisabled(True)
|
||||
|
||||
def updateTDR(self):
|
||||
c = 299792458
|
||||
if len(self.data) < 2:
|
||||
return
|
||||
|
||||
if self.tdr_velocity_dropdown.currentData() == -1:
|
||||
self.tdr_velocity_input.setDisabled(False)
|
||||
else:
|
||||
self.tdr_velocity_input.setDisabled(True)
|
||||
self.tdr_velocity_input.setText(str(self.tdr_velocity_dropdown.currentData()))
|
||||
|
||||
try:
|
||||
v = float(self.tdr_velocity_input.text())
|
||||
except ValueError:
|
||||
return
|
||||
|
||||
step_size = self.data[1].freq - self.data[0].freq
|
||||
if step_size == 0:
|
||||
self.tdr_result_label.setText("")
|
||||
self.lister.appendPlainText("Cannot compute cable length at 0 span")
|
||||
return
|
||||
|
||||
s11 = []
|
||||
for d in self.data:
|
||||
s11.append(np.complex(d.re, d.im))
|
||||
|
||||
window = np.blackman(len(self.data))
|
||||
|
||||
windowed_s11 = window * s11
|
||||
|
||||
td = np.abs(np.fft.ifft(windowed_s11, 2**14))
|
||||
|
||||
time_axis = np.linspace(0, 1/step_size, 2**14)
|
||||
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(td)
|
||||
|
||||
self.tdr_result_label.setText(str(round(distance_axis[index_peak]/2, 3)) + " m")
|
||||
|
||||
def updateCenterSpan(self):
|
||||
fstart = self.parseFrequency(self.sweepStartInput.text())
|
||||
fstop = self.parseFrequency(self.sweepEndInput.text())
|
||||
|
@ -1049,6 +975,10 @@ class NanoVNASaver(QtWidgets.QWidget):
|
|||
self.fileWindow.show()
|
||||
QtWidgets.QApplication.setActiveWindow(self.fileWindow)
|
||||
|
||||
def displayTDRWindow(self):
|
||||
self.tdr_window.show()
|
||||
QtWidgets.QApplication.setActiveWindow(self.tdr_window)
|
||||
|
||||
def closeEvent(self, a0: QtGui.QCloseEvent) -> None:
|
||||
self.worker.stopped = True
|
||||
self.settings.setValue("Marker1Color", self.markers[0].color)
|
||||
|
@ -1189,3 +1119,111 @@ class DisplaySettingsWindow(QtWidgets.QWidget):
|
|||
for c in self.app.charts:
|
||||
c.setBackgroundColor(QtGui.QColor(QtCore.Qt.white))
|
||||
c.setTextColor(QtGui.QColor(QtCore.Qt.black))
|
||||
|
||||
|
||||
class TDRWindow(QtWidgets.QWidget):
|
||||
def __init__(self, app: NanoVNASaver):
|
||||
super().__init__()
|
||||
self.app = app
|
||||
|
||||
self.td = []
|
||||
self.distance_axis = []
|
||||
|
||||
self.setWindowTitle("TDR")
|
||||
|
||||
layout = QtWidgets.QFormLayout()
|
||||
self.setLayout(layout)
|
||||
|
||||
self.tdr_velocity_dropdown = QtWidgets.QComboBox()
|
||||
self.tdr_velocity_dropdown.addItem("Jelly filled (0.64)", 0.64)
|
||||
self.tdr_velocity_dropdown.addItem("Polyethylene (0.66)", 0.66)
|
||||
self.tdr_velocity_dropdown.addItem("PTFE (Teflon) (0.70)", 0.70)
|
||||
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())
|
||||
# Lots of table types added by Larry Goga, AE5CZ
|
||||
self.tdr_velocity_dropdown.addItem("RG-6/U PE 75ohm (Belden 8215) (0.66)", 0.66)
|
||||
self.tdr_velocity_dropdown.addItem("RG-6/U Foam 75 ohm (Belden 9290) (0.81)", 0.81)
|
||||
self.tdr_velocity_dropdown.addItem("RG-8/U PE 75 ohm (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)
|
||||
self.tdr_velocity_dropdown.addItem("RG-8X (Belden9258) (0.82)", 0.82)
|
||||
self.tdr_velocity_dropdown.addItem("RG-11/U 75 ohm Foam HDPE (Belden 9292) (0.84)", 0.84)
|
||||
self.tdr_velocity_dropdown.addItem("RG-58/U PE (Belden 9201) (0.66)", 0.66)
|
||||
self.tdr_velocity_dropdown.addItem("RG-58A/U Foam (Belden 8219) (0.73)", 0.73)
|
||||
self.tdr_velocity_dropdown.addItem("RG-59A/U PE 75 ohm (Belden 8241) (0.66)", 0.66)
|
||||
self.tdr_velocity_dropdown.addItem("RG-59A/U Foam 75 ohm (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("RG316 (0.69)", 0.69)
|
||||
self.tdr_velocity_dropdown.addItem("RG402 (0.695)", 0.695)
|
||||
self.tdr_velocity_dropdown.addItem("LMR-240 (0.84)", 0.84)
|
||||
self.tdr_velocity_dropdown.addItem("LMR-240UF (0.80)", 0.80)
|
||||
self.tdr_velocity_dropdown.addItem("LMR-400 (0.85)", 0.85)
|
||||
self.tdr_velocity_dropdown.addItem("LMR400UF (0.85)", 0.85)
|
||||
self.tdr_velocity_dropdown.addItem("Davis BuryFlex (0.82)", 0.82)
|
||||
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)
|
||||
|
||||
self.tdr_velocity_dropdown.currentIndexChanged.connect(self.updateTDR)
|
||||
|
||||
layout.addRow(self.tdr_velocity_dropdown)
|
||||
|
||||
self.tdr_velocity_input = QtWidgets.QLineEdit()
|
||||
self.tdr_velocity_input.setDisabled(True)
|
||||
self.tdr_velocity_input.setText("0.66")
|
||||
self.tdr_velocity_input.textChanged.connect(self.app.dataUpdated)
|
||||
|
||||
layout.addRow("Velocity factor", self.tdr_velocity_input)
|
||||
|
||||
self.tdr_result_label = QtWidgets.QLabel()
|
||||
layout.addRow("Estimated cable length:", self.tdr_result_label)
|
||||
|
||||
layout.addRow(self.app.tdr_chart)
|
||||
|
||||
def updateTDR(self):
|
||||
c = 299792458
|
||||
if len(self.app.data) < 2:
|
||||
return
|
||||
|
||||
if self.tdr_velocity_dropdown.currentData() == -1:
|
||||
self.tdr_velocity_input.setDisabled(False)
|
||||
else:
|
||||
self.tdr_velocity_input.setDisabled(True)
|
||||
self.tdr_velocity_input.setText(str(self.tdr_velocity_dropdown.currentData()))
|
||||
|
||||
try:
|
||||
v = float(self.tdr_velocity_input.text())
|
||||
except ValueError:
|
||||
return
|
||||
|
||||
step_size = self.app.data[1].freq - self.app.data[0].freq
|
||||
if step_size == 0:
|
||||
self.tdr_result_label.setText("")
|
||||
self.lister.appendPlainText("Cannot compute cable length at 0 span")
|
||||
return
|
||||
|
||||
s11 = []
|
||||
for d in self.app.data:
|
||||
s11.append(np.complex(d.re, d.im))
|
||||
|
||||
window = np.blackman(len(self.app.data))
|
||||
|
||||
windowed_s11 = window * s11
|
||||
|
||||
self.td = np.abs(np.fft.ifft(windowed_s11, 2**14))
|
||||
|
||||
time_axis = np.linspace(0, 1/step_size, 2**14)
|
||||
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)
|
||||
|
||||
self.tdr_result_label.setText(str(round(self.distance_axis[index_peak]/2, 3)) + " m")
|
||||
self.app.tdr_result_label.setText(str(round(self.distance_axis[index_peak] / 2, 3)) + " m")
|
||||
self.app.tdr_chart.update()
|
Ładowanie…
Reference in New Issue