nanovna-saver/NanoVNASaver/Analysis/ResonanceAnalysis.py

138 wiersze
4.9 KiB
Python

# NanoVNASaver
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
# Copyright (C) 2020ff 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 <https://www.gnu.org/licenses/>.
import os
import csv
import logging
from typing import List
from PyQt5 import QtWidgets
import NanoVNASaver.AnalyticTools as at
from NanoVNASaver.Analysis.Base import Analysis, QHLine
from NanoVNASaver.Formatting import (
format_frequency, format_complex_imp,
format_resistance)
from NanoVNASaver.RFTools import reflection_coefficient
logger = logging.getLogger(__name__)
def format_resistence_neg(x):
return format_resistance(x, allow_negative=True)
def vswr_transformed(z, ratio=49) -> float:
refl = reflection_coefficient(z / ratio)
mag = abs(refl)
return 1 if mag == 1 else (1 + mag) / (1 - mag)
class ResonanceAnalysis(Analysis):
def __init__(self, app):
super().__init__(app)
self.crossing: List[int] = []
self.filename = ""
self._widget = QtWidgets.QWidget()
self.layout = QtWidgets.QFormLayout()
self._widget.setLayout(self.layout)
self.input_description = QtWidgets.QLineEdit("")
self.checkbox_move_marker = QtWidgets.QCheckBox()
self.layout.addRow(QtWidgets.QLabel("<b>Settings</b>"))
self.layout.addRow("Description", self.input_description)
self.layout.addRow(QHLine())
self.layout.addRow(QHLine())
self.results_label = QtWidgets.QLabel("<b>Results</b>")
self.layout.addRow(self.results_label)
def _get_data(self, index):
s11 = self.app.data.s11
my_data = {
"freq": s11[index].freq,
"s11": s11[index].z,
"lambda": s11[index].wavelength,
"impedance": s11[index].impedance(),
"vswr": s11[index].vswr,
}
my_data["vswr_49"] = vswr_transformed(
my_data["impedance"], 49)
my_data["vswr_4"] = vswr_transformed(
my_data["impedance"], 4)
my_data["r"] = my_data["impedance"].real
my_data["x"] = my_data["impedance"].imag
return my_data
def runAnalysis(self):
self.reset()
self.filename = os.path.join(
"/tmp/", f"{self.input_description.text()}.csv"
) if self.input_description.text() else ""
results_header = self.layout.indexOf(self.results_label)
logger.debug("Results start at %d, out of %d",
results_header, self.layout.rowCount())
for _ in range(results_header, self.layout.rowCount()):
self.layout.removeRow(self.layout.rowCount() - 1)
self.crossing = at.zero_crossings([d.phase for d in self.app.data.s11])
logger.debug("Found %d sections ",
len(self.crossing))
if not self.crossing:
self.layout.addRow(QtWidgets.QLabel(
"No resonance found"))
return
self.do_resonance_analysis()
def do_resonance_analysis(self):
extended_data = []
for m in self.crossing:
start, lowest, end = m
my_data = self._get_data(lowest)
s11_low = self.app.data.s11[lowest]
extended_data.append(my_data)
if start != end:
logger.debug(
"Section from %d to %d, lowest at %d",
start, end, lowest)
self.layout.addRow(
"Resonance",
QtWidgets.QLabel(
f"{format_frequency(s11_low.freq)}"
f" ({format_complex_imp(s11_low.impedance())})"))
else:
self.layout.addRow("Resonance", QtWidgets.QLabel(
format_frequency(self.app.data.s11[lowest].freq)))
self.layout.addWidget(QHLine())
# Remove the final separator line
self.layout.removeRow(self.layout.rowCount() - 1)
if self.filename and extended_data:
with open(
self.filename, 'w', encoding='utf-8', newline=''
) as csvfile:
fieldnames = extended_data[0].keys()
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
writer.writeheader()
for row in extended_data:
writer.writerow(row)