kopia lustrzana https://github.com/NanoVNA-Saver/nanovna-saver
566 wiersze
23 KiB
Python
566 wiersze
23 KiB
Python
# NanoVNASaver - a python program to view and export Touchstone data from a NanoVNA
|
|
# Copyright (C) 2019. Rune B. Broberg
|
|
#
|
|
# 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 <https://www.gnu.org/licenses/>.
|
|
import collections
|
|
import math
|
|
import threading
|
|
from time import sleep
|
|
from typing import List
|
|
|
|
import numpy as np
|
|
import serial
|
|
from PyQt5 import QtWidgets, QtCore, QtGui
|
|
|
|
from Marker import Marker
|
|
from SmithChart import SmithChart
|
|
from SweepWorker import SweepWorker
|
|
from LogMagChart import LogMagChart
|
|
|
|
Datapoint = collections.namedtuple('Datapoint', 'freq re im')
|
|
|
|
|
|
class NanoVNASaver(QtWidgets.QWidget):
|
|
def __init__(self):
|
|
super().__init__()
|
|
|
|
self.threadpool = QtCore.QThreadPool()
|
|
print("Max thread count " + str(self.threadpool.maxThreadCount()))
|
|
self.worker = SweepWorker(self)
|
|
|
|
self.noSweeps = 1 # Number of sweeps to run
|
|
|
|
self.serialLock = threading.Lock()
|
|
self.serial = serial.Serial()
|
|
|
|
self.dataLock = threading.Lock()
|
|
self.values = []
|
|
self.frequencies = []
|
|
self.data : List[Datapoint] = []
|
|
self.data21 : List[Datapoint] = []
|
|
self.markers = []
|
|
|
|
self.serialPort = "COM11"
|
|
# self.serialSpeed = "115200"
|
|
|
|
self.color = QtGui.QColor(160, 140, 20, 128)
|
|
|
|
self.setWindowTitle("NanoVNA Saver")
|
|
layout = QtWidgets.QGridLayout()
|
|
self.setLayout(layout)
|
|
|
|
self.smithChart = SmithChart("S11")
|
|
self.s21SmithChart = SmithChart("S21")
|
|
self.s11LogMag = LogMagChart("S11 Return Loss")
|
|
self.s21LogMag = LogMagChart("S21 Gain")
|
|
|
|
left_column = QtWidgets.QVBoxLayout()
|
|
right_column = QtWidgets.QVBoxLayout()
|
|
|
|
layout.addLayout(left_column, 0, 0)
|
|
layout.addLayout(right_column, 0, 1)
|
|
|
|
################################################################################################################
|
|
# Sweep control
|
|
################################################################################################################
|
|
|
|
sweep_control_box = QtWidgets.QGroupBox()
|
|
sweep_control_box.setMaximumWidth(400)
|
|
sweep_control_box.setTitle("Sweep control")
|
|
sweep_control_layout = QtWidgets.QFormLayout(sweep_control_box)
|
|
|
|
self.sweepStartInput = QtWidgets.QLineEdit("")
|
|
self.sweepStartInput.setAlignment(QtCore.Qt.AlignRight)
|
|
|
|
sweep_control_layout.addRow(QtWidgets.QLabel("Sweep start"), self.sweepStartInput)
|
|
|
|
self.sweepEndInput = QtWidgets.QLineEdit("")
|
|
self.sweepEndInput.setAlignment(QtCore.Qt.AlignRight)
|
|
|
|
sweep_control_layout.addRow(QtWidgets.QLabel("Sweep end"), self.sweepEndInput)
|
|
|
|
self.sweepCountInput = QtWidgets.QLineEdit("")
|
|
self.sweepCountInput.setAlignment(QtCore.Qt.AlignRight)
|
|
self.sweepCountInput.setText("1")
|
|
|
|
sweep_control_layout.addRow(QtWidgets.QLabel("Sweep count"), self.sweepCountInput)
|
|
|
|
self.btnColorPicker = QtWidgets.QPushButton("█")
|
|
self.btnColorPicker.setFixedWidth(20)
|
|
self.setSweepColor(self.color)
|
|
self.btnColorPicker.clicked.connect(lambda: self.setSweepColor(QtWidgets.QColorDialog.getColor(self.color, options=QtWidgets.QColorDialog.ShowAlphaChannel)))
|
|
|
|
sweep_control_layout.addRow("Sweep color", self.btnColorPicker)
|
|
|
|
self.sweepProgressBar = QtWidgets.QProgressBar()
|
|
self.sweepProgressBar.setMaximum(100)
|
|
self.sweepProgressBar.setValue(0)
|
|
sweep_control_layout.addRow(self.sweepProgressBar)
|
|
|
|
self.btnSweep = QtWidgets.QPushButton("Sweep")
|
|
self.btnSweep.clicked.connect(self.sweep)
|
|
sweep_control_layout.addRow(self.btnSweep)
|
|
|
|
left_column.addWidget(sweep_control_box)
|
|
|
|
################################################################################################################
|
|
# Marker control
|
|
################################################################################################################
|
|
|
|
marker_control_box = QtWidgets.QGroupBox()
|
|
marker_control_box.setTitle("Markers")
|
|
marker_control_box.setMaximumWidth(400)
|
|
marker_control_layout = QtWidgets.QFormLayout(marker_control_box)
|
|
|
|
marker1 = Marker("Marker 1", QtGui.QColor(255, 0, 20))
|
|
marker1.updated.connect(self.dataUpdated)
|
|
label, layout = marker1.getRow()
|
|
marker_control_layout.addRow(label, layout)
|
|
self.markers.append(marker1)
|
|
|
|
marker2 = Marker("Marker 2", QtGui.QColor(20, 0, 255))
|
|
marker2.updated.connect(self.dataUpdated)
|
|
label, layout = marker2.getRow()
|
|
marker_control_layout.addRow(label, layout)
|
|
self.markers.append(marker2)
|
|
|
|
self.smithChart.setMarkers(self.markers)
|
|
self.s21SmithChart.setMarkers(self.markers)
|
|
|
|
self.marker1label = QtWidgets.QLabel("")
|
|
marker_control_layout.addRow(QtWidgets.QLabel("Marker 1: "), self.marker1label)
|
|
|
|
self.marker2label = QtWidgets.QLabel("")
|
|
marker_control_layout.addRow(QtWidgets.QLabel("Marker 2: "), self.marker2label)
|
|
|
|
left_column.addWidget(marker_control_box)
|
|
|
|
################################################################################################################
|
|
# Statistics/analysis
|
|
################################################################################################################
|
|
|
|
s11_control_box = QtWidgets.QGroupBox()
|
|
s11_control_box.setTitle("S11")
|
|
s11_control_layout = QtWidgets.QFormLayout()
|
|
s11_control_box.setLayout(s11_control_layout)
|
|
|
|
self.s11_min_swr_label = QtWidgets.QLabel()
|
|
s11_control_layout.addRow("Min VSWR:", self.s11_min_swr_label)
|
|
self.s11_min_rl_label = QtWidgets.QLabel()
|
|
s11_control_layout.addRow("Return loss:", self.s11_min_rl_label)
|
|
|
|
left_column.addWidget(s11_control_box)
|
|
|
|
s21_control_box = QtWidgets.QGroupBox()
|
|
s21_control_box.setTitle("S21")
|
|
s21_control_layout = QtWidgets.QFormLayout()
|
|
s21_control_box.setLayout(s21_control_layout)
|
|
|
|
self.s21_min_gain_label = QtWidgets.QLabel()
|
|
s21_control_layout.addRow("Min gain:", self.s21_min_gain_label)
|
|
|
|
self.s21_max_gain_label = QtWidgets.QLabel()
|
|
s21_control_layout.addRow("Max gain:", self.s21_max_gain_label)
|
|
|
|
left_column.addWidget(s21_control_box)
|
|
|
|
tdr_control_box = QtWidgets.QGroupBox()
|
|
tdr_control_box.setTitle("TDR")
|
|
tdr_control_layout = QtWidgets.QFormLayout()
|
|
tdr_control_box.setLayout(tdr_control_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.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)
|
|
|
|
left_column.addWidget(tdr_control_box)
|
|
|
|
################################################################################################################
|
|
# Spacer
|
|
################################################################################################################
|
|
|
|
left_column.addSpacerItem(QtWidgets.QSpacerItem(1, 1, QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Expanding))
|
|
|
|
################################################################################################################
|
|
# Serial control
|
|
################################################################################################################
|
|
|
|
serial_control_box = QtWidgets.QGroupBox()
|
|
serial_control_box.setMaximumWidth(400)
|
|
serial_control_box.setTitle("Serial port control")
|
|
serial_control_layout = QtWidgets.QFormLayout(serial_control_box)
|
|
self.serialPortInput = QtWidgets.QLineEdit(self.serialPort)
|
|
self.serialPortInput.setAlignment(QtCore.Qt.AlignRight)
|
|
# self.serialSpeedInput = QtWidgets.QLineEdit(str(self.serialSpeed))
|
|
# self.serialSpeedInput.setValidator(QtGui.QIntValidator())
|
|
# self.serialSpeedInput.setAlignment(QtCore.Qt.AlignRight)
|
|
serial_control_layout.addRow(QtWidgets.QLabel("Serial port"), self.serialPortInput)
|
|
# serial_control_layout.addRow(QtWidgets.QLabel("Speed"), self.serialSpeedInput)
|
|
|
|
self.btnSerialToggle = QtWidgets.QPushButton("Open serial")
|
|
self.btnSerialToggle.clicked.connect(self.serialButtonClick)
|
|
serial_control_layout.addRow(self.btnSerialToggle)
|
|
|
|
left_column.addWidget(serial_control_box)
|
|
|
|
################################################################################################################
|
|
# File control
|
|
################################################################################################################
|
|
|
|
file_control_box = QtWidgets.QGroupBox()
|
|
file_control_box.setTitle("Export file")
|
|
file_control_box.setMaximumWidth(400)
|
|
file_control_layout = QtWidgets.QFormLayout(file_control_box)
|
|
self.fileNameInput = QtWidgets.QLineEdit("")
|
|
self.fileNameInput.setAlignment(QtCore.Qt.AlignRight)
|
|
btnFilePicker = QtWidgets.QPushButton("...")
|
|
btnFilePicker.setMaximumWidth(25)
|
|
btnFilePicker.clicked.connect(self.pickFile)
|
|
fileNameLayout = QtWidgets.QHBoxLayout()
|
|
fileNameLayout.addWidget(self.fileNameInput)
|
|
fileNameLayout.addWidget(btnFilePicker)
|
|
|
|
file_control_layout.addRow(QtWidgets.QLabel("Filename"), fileNameLayout)
|
|
|
|
self.btnExportFile = QtWidgets.QPushButton("Export data S1P")
|
|
self.btnExportFile.clicked.connect(self.exportFileS1P)
|
|
file_control_layout.addRow(self.btnExportFile)
|
|
|
|
self.btnExportFile = QtWidgets.QPushButton("Export data S2P")
|
|
self.btnExportFile.clicked.connect(self.exportFileS2P)
|
|
file_control_layout.addRow(self.btnExportFile)
|
|
|
|
left_column.addWidget(file_control_box)
|
|
|
|
################################################################################################################
|
|
# Right side
|
|
################################################################################################################
|
|
|
|
self.lister = QtWidgets.QPlainTextEdit()
|
|
self.lister.setFixedHeight(100)
|
|
charts = QtWidgets.QGridLayout()
|
|
charts.addWidget(self.smithChart, 0, 0)
|
|
charts.addWidget(self.s21SmithChart, 1, 0)
|
|
charts.addWidget(self.s11LogMag, 0, 1)
|
|
charts.addWidget(self.s21LogMag, 1, 1)
|
|
|
|
self.s11LogMag.setMarkers(self.markers)
|
|
self.s21LogMag.setMarkers(self.markers)
|
|
|
|
right_column.addLayout(charts)
|
|
right_column.addWidget(self.lister)
|
|
|
|
self.worker.signals.updated.connect(self.dataUpdated)
|
|
self.worker.signals.finished.connect(self.sweepFinished)
|
|
|
|
def pickFile(self):
|
|
filename, _ = QtWidgets.QFileDialog.getSaveFileName(directory=self.fileNameInput.text(), filter="Touchstone Files (*.s1p *.s2p);;All files (*.*)")
|
|
self.fileNameInput.setText(filename)
|
|
|
|
def exportFileS1P(self):
|
|
print("Save file to " + self.fileNameInput.text())
|
|
if len(self.data) == 0:
|
|
self.lister.appendPlainText("No data stored, nothing written.")
|
|
return
|
|
filename = self.fileNameInput.text()
|
|
if filename == "":
|
|
self.lister.appendPlainText("No filename entered.")
|
|
return
|
|
try:
|
|
file = open(filename, "w+")
|
|
self.lister.clear()
|
|
self.lister.appendPlainText("# Hz S RI R 50")
|
|
file.write("# Hz S RI R 50\n")
|
|
for i in range(len(self.data)):
|
|
if i == 0 or self.data[i].freq != self.data[i-1].freq:
|
|
self.lister.appendPlainText(str(self.data[i].freq) + " " + str(self.data[i].re) + " " + str(self.data[i].im))
|
|
file.write(str(self.data[i].freq) + " " + str(self.data[i].re) + " " + str(self.data[i].im) + "\n")
|
|
file.close()
|
|
except Exception as e:
|
|
print("Error during file export: " + str(e))
|
|
self.lister.appendPlainText("Error during file export: " + str(e))
|
|
return
|
|
|
|
self.lister.appendPlainText("")
|
|
self.lister.appendPlainText("File " + filename + " written.")
|
|
|
|
def exportFileS2P(self):
|
|
print("Save file to " + self.fileNameInput.text())
|
|
if (len(self.data) == 0):
|
|
self.lister.appendPlainText("No data stored, nothing written.")
|
|
return
|
|
filename = self.fileNameInput.text()
|
|
if filename == "":
|
|
self.lister.appendPlainText("No filename entered.")
|
|
return
|
|
try:
|
|
file = open(filename, "w+")
|
|
self.lister.clear()
|
|
self.lister.appendPlainText("# Hz S RI R 50")
|
|
file.write("# Hz S RI R 50\n")
|
|
for i in range(len(self.data)):
|
|
if i == 0 or self.data[i].freq != self.data[i-1].freq:
|
|
self.lister.appendPlainText(str(self.data[i].freq) + " " + str(self.data[i].re) + " " + str(self.data[i].im) + " " +
|
|
str(self.data21[i].re) + " " + str(self.data21[i].im) + " 0 0 0 0")
|
|
file.write(str(self.data[i].freq) + " " + str(self.data[i].re) + " " + str(self.data[i].im) + " " +
|
|
str(self.data21[i].re) + " " + str(self.data21[i].im) + " 0 0 0 0\n")
|
|
file.close()
|
|
except Exception as e:
|
|
print("Error during file export: " + str(e))
|
|
self.lister.appendPlainText("Error during file export: " + str(e))
|
|
return
|
|
|
|
self.lister.appendPlainText("")
|
|
self.lister.appendPlainText("File " + filename + " written.")
|
|
|
|
def serialButtonClick(self):
|
|
if self.serial.is_open:
|
|
self.stopSerial()
|
|
else:
|
|
self.startSerial()
|
|
return
|
|
|
|
def startSerial(self):
|
|
self.lister.appendPlainText("Opening serial port " + self.serialPort)
|
|
|
|
if self.serialLock.acquire():
|
|
self.serialPort = self.serialPortInput.text()
|
|
try:
|
|
self.serial = serial.Serial(port=self.serialPort, baudrate=115200)
|
|
self.serial.timeout = 0.05
|
|
except serial.SerialException as exc:
|
|
self.lister.appendPlainText("Tried to open " + self.serialPort + " and failed.")
|
|
self.serialLock.release()
|
|
return
|
|
self.btnSerialToggle.setText("Close serial")
|
|
|
|
self.serialLock.release()
|
|
sleep(0.05)
|
|
|
|
frequencies = self.readValues("frequencies")
|
|
|
|
self.sweepStartInput.setText(str(frequencies[0]))
|
|
self.sweepEndInput.setText(str(frequencies[100]))
|
|
|
|
self.sweep()
|
|
return
|
|
|
|
def stopSerial(self):
|
|
if self.serialLock.acquire():
|
|
self.serial.close()
|
|
self.serialLock.release()
|
|
self.btnSerialToggle.setText("Open serial")
|
|
|
|
def writeSerial(self, command):
|
|
if not self.serial.is_open:
|
|
print("Warning: Writing without serial port being opened (" + command + ")")
|
|
return
|
|
if self.serialLock.acquire():
|
|
try:
|
|
self.serial.write(str(command + "\r").encode('ascii'))
|
|
self.serial.readline()
|
|
except serial.SerialException as exc:
|
|
print("Exception received")
|
|
self.serialLock.release()
|
|
return
|
|
|
|
def setSweep(self, start, stop):
|
|
# print("Sending: " + "sweep " + str(start) + " " + str(stop) + " 101")
|
|
self.writeSerial("sweep " + str(start) + " " + str(stop) + " 101")
|
|
|
|
def sweep(self):
|
|
# Run the serial port update
|
|
if not self.serial.is_open:
|
|
return
|
|
|
|
self.sweepProgressBar.setValue(0)
|
|
self.btnSweep.setDisabled(True)
|
|
|
|
self.threadpool.start(self.worker)
|
|
|
|
def readValues(self, value):
|
|
if self.serialLock.acquire():
|
|
try:
|
|
data = "a"
|
|
while data != "":
|
|
data = self.serial.readline().decode('ascii')
|
|
|
|
# Then send the command to read data
|
|
self.serial.write(str(value + "\r").encode('ascii'))
|
|
except serial.SerialException as exc:
|
|
print("Exception received: " + str(exc))
|
|
result = ""
|
|
data = ""
|
|
sleep(0.01)
|
|
while "ch>" not in data:
|
|
data = self.serial.readline().decode('ascii')
|
|
result += data
|
|
values = result.split("\r\n")
|
|
self.serialLock.release()
|
|
return values[1:102]
|
|
|
|
def saveData(self, data, data12):
|
|
if self.dataLock.acquire(blocking=True):
|
|
self.data = data
|
|
self.data21 = data12
|
|
else:
|
|
print("ERROR: Failed acquiring data lock while saving.")
|
|
self.dataLock.release()
|
|
|
|
def dataUpdated(self):
|
|
if self.dataLock.acquire(blocking=True):
|
|
for m in self.markers:
|
|
m.findLocation(self.data)
|
|
# TODO: Make a neater solution for showing data for markers
|
|
if self.markers[0].location != -1:
|
|
im50, re50, vswr = self.vswr(self.data[self.markers[0].location])
|
|
self.marker1label.setText(str(round(re50, 3)) + " + j" + str(round(im50, 3)) + " VSWR: 1:" + str(round(vswr, 3)))
|
|
|
|
if self.markers[1].location != -1:
|
|
im50, re50, vswr = self.vswr(self.data[self.markers[1].location])
|
|
self.marker2label.setText(str(round(re50, 3)) + " + j" + str(round(im50, 3)) + " VSWR: 1:" + str(round(vswr, 3)))
|
|
|
|
self.smithChart.setData(self.data)
|
|
self.s21SmithChart.setData(self.data21)
|
|
self.s11LogMag.setData(self.data)
|
|
self.s21LogMag.setData(self.data21)
|
|
self.sweepProgressBar.setValue(self.worker.percentage)
|
|
self.updateTDR()
|
|
# Find the minimum S11 VSWR:
|
|
minVSWR = 100
|
|
minVSWRfreq = -1
|
|
for d in self.data:
|
|
_, _, vswr = self.vswr(d)
|
|
if vswr < minVSWR:
|
|
minVSWR = vswr
|
|
minVSWRfreq = d.freq
|
|
|
|
if minVSWRfreq > -1:
|
|
self.s11_min_swr_label.setText(str(round(minVSWR, 3)) + " @ " + str(minVSWRfreq) + " Hz")
|
|
self.s11_min_rl_label.setText(str(round(20*math.log10((minVSWR-1)/(minVSWR+1)), 3)) + " dB")
|
|
|
|
minGain = 100
|
|
minGainFreq = -1
|
|
maxGain = -100
|
|
maxGainFreq = -1
|
|
for d in self.data21:
|
|
gain = self.gain(d)
|
|
if gain > maxGain:
|
|
maxGain = gain
|
|
maxGainFreq = d.freq
|
|
if gain < minGain:
|
|
minGain = gain
|
|
minGainFreq = d.freq
|
|
|
|
if maxGainFreq > -1:
|
|
self.s21_min_gain_label.setText(str(round(minGain, 3)) + " dB @ " + str(minGainFreq) + " Hz")
|
|
self.s21_max_gain_label.setText(str(round(maxGain, 3)) + " dB @ " + str(maxGainFreq) + " Hz")
|
|
|
|
else:
|
|
print("ERROR: Failed acquiring data lock while updating")
|
|
self.dataLock.release()
|
|
|
|
@staticmethod
|
|
def vswr(data: Datapoint):
|
|
re = data.re
|
|
im = data.im
|
|
re50 = 50 * (1 - re * re - im * im) / (1 + re * re + im * im - 2 * re)
|
|
im50 = 50 * (2 * im) / (1 + re * re + im * im - 2 * re)
|
|
mag = math.sqrt(re * re + im * im)
|
|
vswr = (1 + mag) / (1 - mag)
|
|
return im50, re50, vswr
|
|
|
|
@staticmethod
|
|
def gain(data: Datapoint):
|
|
re = data.re
|
|
im = data.im
|
|
re50 = 50 * (1 - re * re - im * im) / (1 + re * re + im * im - 2 * re)
|
|
im50 = 50 * (2 * im) / (1 + re * re + im * im - 2 * re)
|
|
# Calculate the gain / reflection coefficient
|
|
mag = math.sqrt((re50 - 50) * (re50 - 50) + im50 * im50) / math.sqrt(
|
|
(re50 + 50) * (re50 + 50) + im50 * im50)
|
|
return(20 * math.log10(mag))
|
|
|
|
def sweepFinished(self):
|
|
self.sweepProgressBar.setValue(100)
|
|
self.btnSweep.setDisabled(False)
|
|
|
|
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
|
|
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 setSweepColor(self, color : QtGui.QColor):
|
|
if color.isValid():
|
|
self.color = color
|
|
p = self.btnColorPicker.palette()
|
|
p.setColor(QtGui.QPalette.ButtonText, color)
|
|
self.btnColorPicker.setPalette(p)
|
|
|
|
self.smithChart.setSweepColor(color)
|
|
self.s21SmithChart.setSweepColor(color)
|
|
self.s11LogMag.setSweepColor(color)
|
|
self.s21LogMag.setSweepColor(color) |