From d80324600c30dbd4cf6f6150f24c80f40f29bab5 Mon Sep 17 00:00:00 2001 From: Roel Jordans Date: Fri, 10 Feb 2023 16:30:21 +0100 Subject: [PATCH 1/6] Re-organize RI chart to have RIZ specialization - separate impedance plotting specific bits - preparation new chart to plot mu' and mu'' for given core dimensions --- NanoVNASaver/Charts/RI.py | 55 ++--------- NanoVNASaver/Charts/RIZ.py | 98 +++++++++++++++++++ .../Charts/{RISeries.py => RIZSeries.py} | 4 +- .../Charts/{RIShunt.py => RIZShunt.py} | 4 +- NanoVNASaver/Charts/__init__.py | 5 +- NanoVNASaver/NanoVNASaver.py | 8 +- 6 files changed, 115 insertions(+), 59 deletions(-) create mode 100644 NanoVNASaver/Charts/RIZ.py rename NanoVNASaver/Charts/{RISeries.py => RIZSeries.py} (91%) rename NanoVNASaver/Charts/{RIShunt.py => RIZShunt.py} (91%) diff --git a/NanoVNASaver/Charts/RI.py b/NanoVNASaver/Charts/RI.py index 102f51fc..fab3bb0a 100644 --- a/NanoVNASaver/Charts/RI.py +++ b/NanoVNASaver/Charts/RI.py @@ -72,33 +72,7 @@ def __init__(self, name=""): mode_group.addAction(self.y_action_fixed_span) self.y_menu.addAction(self.y_action_automatic) self.y_menu.addAction(self.y_action_fixed_span) - self.y_menu.addSeparator() - self.action_set_fixed_maximum_real = QtWidgets.QAction( - f"Maximum R ({self.maxDisplayReal})") - self.action_set_fixed_maximum_real.triggered.connect( - self.setMaximumRealValue) - - self.action_set_fixed_minimum_real = QtWidgets.QAction( - f"Minimum R ({self.minDisplayReal})") - self.action_set_fixed_minimum_real.triggered.connect( - self.setMinimumRealValue) - - self.action_set_fixed_maximum_imag = QtWidgets.QAction( - f"Maximum jX ({self.maxDisplayImag})") - self.action_set_fixed_maximum_imag.triggered.connect( - self.setMaximumImagValue) - - self.action_set_fixed_minimum_imag = QtWidgets.QAction( - f"Minimum jX ({self.minDisplayImag})") - self.action_set_fixed_minimum_imag.triggered.connect( - self.setMinimumImagValue) - - self.y_menu.addAction(self.action_set_fixed_maximum_real) - self.y_menu.addAction(self.action_set_fixed_minimum_real) - self.y_menu.addSeparator() - self.y_menu.addAction(self.action_set_fixed_maximum_imag) - self.y_menu.addAction(self.action_set_fixed_minimum_imag) def copy(self): new_chart: RealImaginaryChart = super().copy() @@ -109,23 +83,6 @@ def copy(self): new_chart.minDisplayImag = self.minDisplayImag return new_chart - def drawChart(self, qp: QtGui.QPainter): - qp.setPen(QtGui.QPen(Chart.color.text)) - qp.drawText(self.leftMargin + 5, 15, - f"{self.name} (\N{OHM SIGN})") - qp.drawText(10, 15, "R") - qp.drawText(self.leftMargin + self.dim.width + 10, 15, "X") - qp.setPen(QtGui.QPen(Chart.color.foreground)) - qp.drawLine(self.leftMargin, - self.topMargin - 5, - self.leftMargin, - self.topMargin + self.dim.height + 5) - qp.drawLine(self.leftMargin - 5, - self.topMargin + self.dim.height, - self.leftMargin + self.dim.width + 5, - self.topMargin + self.dim.height) - self.drawTitle(qp) - def drawValues(self, qp: QtGui.QPainter): if not self.data and not self.reference: return @@ -333,7 +290,7 @@ def find_scaling(self): max_real = 0 max_imag = -1000 for d in self.data: - imp = self.impedance(d) + imp = self.value(d) re, im = imp.real, imp.imag if math.isinf(re): # Avoid infinite scales continue @@ -345,7 +302,7 @@ def find_scaling(self): for d in self.reference: if d.freq < self.fstart or d.freq > self.fstop: continue - imp = self.impedance(d) + imp = self.value(d) re, im = imp.real, imp.imag if math.isinf(re): # Avoid infinite scales continue @@ -393,12 +350,12 @@ def imag_scaling_constraints(self, min_imag, max_imag): return min_imag, max_imag def getImYPosition(self, d: Datapoint) -> int: - im = self.impedance(d).imag + im = self.value(d).imag return int(self.topMargin + (self.max_imag - im) / self.span_imag * self.dim.height) def getReYPosition(self, d: Datapoint) -> int: - re = self.impedance(d).real + re = self.value(d).real return int(self.topMargin + (self.max_real - re) / self.span_real * self.dim.height if math.isfinite(re) else self.topMargin) @@ -521,5 +478,5 @@ def contextMenuEvent(self, event): f"Maximum jX ({self.maxDisplayImag})") self.menu.exec_(event.globalPos()) - def impedance(self, p: Datapoint) -> complex: - return p.impedance() + def value(self, p: Datapoint) -> complex: + raise NotImplementedError() diff --git a/NanoVNASaver/Charts/RIZ.py b/NanoVNASaver/Charts/RIZ.py new file mode 100644 index 00000000..049c5d79 --- /dev/null +++ b/NanoVNASaver/Charts/RIZ.py @@ -0,0 +1,98 @@ +# 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 logging + +from PyQt5 import QtWidgets, QtGui + +from NanoVNASaver.Formatting import format_frequency_chart +from NanoVNASaver.RFTools import Datapoint +from NanoVNASaver.Charts.Chart import Chart + +from .RI import RealImaginaryChart + +logger = logging.getLogger(__name__) + +class RealImaginaryZChart(RealImaginaryChart): + def __init__(self, name=""): + super().__init__(name) + self.y_menu.addSeparator() + + self.action_set_fixed_maximum_real = QtWidgets.QAction( + f"Maximum R ({self.maxDisplayReal})") + self.action_set_fixed_maximum_real.triggered.connect( + self.setMaximumRealValue) + + self.action_set_fixed_minimum_real = QtWidgets.QAction( + f"Minimum R ({self.minDisplayReal})") + self.action_set_fixed_minimum_real.triggered.connect( + self.setMinimumRealValue) + + self.action_set_fixed_maximum_imag = QtWidgets.QAction( + f"Maximum jX ({self.maxDisplayImag})") + self.action_set_fixed_maximum_imag.triggered.connect( + self.setMaximumImagValue) + + self.action_set_fixed_minimum_imag = QtWidgets.QAction( + f"Minimum jX ({self.minDisplayImag})") + self.action_set_fixed_minimum_imag.triggered.connect( + self.setMinimumImagValue) + + self.y_menu.addAction(self.action_set_fixed_maximum_real) + self.y_menu.addAction(self.action_set_fixed_minimum_real) + self.y_menu.addSeparator() + self.y_menu.addAction(self.action_set_fixed_maximum_imag) + self.y_menu.addAction(self.action_set_fixed_minimum_imag) + + def drawChart(self, qp: QtGui.QPainter): + qp.setPen(QtGui.QPen(Chart.color.text)) + qp.drawText(self.leftMargin + 5, 15, + f"{self.name} (\N{OHM SIGN})") + qp.drawText(10, 15, "R") + qp.drawText(self.leftMargin + self.dim.width + 10, 15, "X") + qp.setPen(QtGui.QPen(Chart.color.foreground)) + qp.drawLine(self.leftMargin, + self.topMargin - 5, + self.leftMargin, + self.topMargin + self.dim.height + 5) + qp.drawLine(self.leftMargin-5, + self.topMargin + self.dim.height, + self.leftMargin + self.dim.width + 5, + self.topMargin + self.dim.height) + self.drawTitle(qp) + + def contextMenuEvent(self, event): + self.action_set_fixed_start.setText( + f"Start ({format_frequency_chart(self.minFrequency)})") + self.action_set_fixed_stop.setText( + f"Stop ({format_frequency_chart(self.maxFrequency)})") + self.action_set_fixed_minimum_real.setText( + f"Minimum R ({self.minDisplayReal})") + self.action_set_fixed_maximum_real.setText( + f"Maximum R ({self.maxDisplayReal})") + self.action_set_fixed_minimum_imag.setText( + f"Minimum jX ({self.minDisplayImag})") + self.action_set_fixed_maximum_imag.setText( + f"Maximum jX ({self.maxDisplayImag})") + self.menu.exec_(event.globalPos()) + + def value(self, p: Datapoint) -> complex: + return self.impedance(p) + + def impedance(self, p: Datapoint) -> complex: + return p.impedance() diff --git a/NanoVNASaver/Charts/RISeries.py b/NanoVNASaver/Charts/RIZSeries.py similarity index 91% rename from NanoVNASaver/Charts/RISeries.py rename to NanoVNASaver/Charts/RIZSeries.py index 245b9372..60f18fcf 100644 --- a/NanoVNASaver/Charts/RISeries.py +++ b/NanoVNASaver/Charts/RIZSeries.py @@ -19,12 +19,12 @@ import logging from NanoVNASaver.RFTools import Datapoint -from .RI import RealImaginaryChart +from .RIZ import RealImaginaryZChart logger = logging.getLogger(__name__) -class RealImaginarySeriesChart(RealImaginaryChart): +class RealImaginaryZSeriesChart(RealImaginaryZChart): def impedance(self, p: Datapoint) -> complex: return p.seriesImpedance() diff --git a/NanoVNASaver/Charts/RIShunt.py b/NanoVNASaver/Charts/RIZShunt.py similarity index 91% rename from NanoVNASaver/Charts/RIShunt.py rename to NanoVNASaver/Charts/RIZShunt.py index 29874253..16092827 100644 --- a/NanoVNASaver/Charts/RIShunt.py +++ b/NanoVNASaver/Charts/RIZShunt.py @@ -19,12 +19,12 @@ import logging from NanoVNASaver.RFTools import Datapoint -from .RI import RealImaginaryChart +from .RIZ import RealImaginaryZChart logger = logging.getLogger(__name__) -class RealImaginaryShuntChart(RealImaginaryChart): +class RealImaginaryZShuntChart(RealImaginaryZChart): def impedance(self, p: Datapoint) -> complex: return p.shuntImpedance() diff --git a/NanoVNASaver/Charts/__init__.py b/NanoVNASaver/Charts/__init__.py index d8ebd651..30c55207 100644 --- a/NanoVNASaver/Charts/__init__.py +++ b/NanoVNASaver/Charts/__init__.py @@ -15,8 +15,9 @@ from .Phase import PhaseChart from .QFactor import QualityFactorChart from .RI import RealImaginaryChart -from .RIShunt import RealImaginaryShuntChart -from .RISeries import RealImaginarySeriesChart +from .RIZ import RealImaginaryZChart +from .RIZShunt import RealImaginaryZShuntChart +from .RIZSeries import RealImaginaryZSeriesChart from .Smith import SmithChart from .SParam import SParameterChart from .TDR import TDRChart diff --git a/NanoVNASaver/NanoVNASaver.py b/NanoVNASaver/NanoVNASaver.py index d14a9eae..796e91ff 100644 --- a/NanoVNASaver/NanoVNASaver.py +++ b/NanoVNASaver/NanoVNASaver.py @@ -45,7 +45,7 @@ MagnitudeChart, MagnitudeZChart, MagnitudeZShuntChart, MagnitudeZSeriesChart, QualityFactorChart, VSWRChart, PermeabilityChart, PolarChart, - RealImaginaryChart, RealImaginaryShuntChart, RealImaginarySeriesChart, + RealImaginaryChart, RealImaginaryZChart, RealImaginaryZShuntChart, RealImaginaryZSeriesChart, SmithChart, SParameterChart, TDRChart, ) from .Calibration import Calibration @@ -151,7 +151,7 @@ def __init__(self): " X/\N{GREEK SMALL LETTER OMEGA}"), "phase": PhaseChart("S11 Phase"), "q_factor": QualityFactorChart("S11 Quality Factor"), - "real_imag": RealImaginaryChart("S11 R+jX"), + "real_imag": RealImaginaryZChart("S11 R+jX"), "smith": SmithChart("S11 Smith Chart"), "s_parameter": SParameterChart("S11 Real/Imaginary"), "vswr": VSWRChart("S11 VSWR"), @@ -163,8 +163,8 @@ def __init__(self): "magnitude": MagnitudeChart("|S21|"), "magnitude_z_shunt": MagnitudeZShuntChart("S21 |Z| shunt"), "magnitude_z_series": MagnitudeZSeriesChart("S21 |Z| series"), - "real_imag_shunt": RealImaginaryShuntChart("S21 R+jX shunt"), - "real_imag_series": RealImaginarySeriesChart( + "real_imag_shunt": RealImaginaryZShuntChart("S21 R+jX shunt"), + "real_imag_series": RealImaginaryZSeriesChart( "S21 R+jX series"), "phase": PhaseChart("S21 Phase"), "polar": PolarChart("S21 Polar Plot"), From 81ff81d5bb8ef3e489f2ec025bcdd8aa72c47fc8 Mon Sep 17 00:00:00 2001 From: Roel Jordans Date: Fri, 10 Feb 2023 18:40:23 +0100 Subject: [PATCH 2/6] Calculating mu of a core based on given dimensions --- NanoVNASaver/Charts/RIMu.py | 170 ++++++++++++++++++++++++++++++++ NanoVNASaver/Charts/__init__.py | 1 + NanoVNASaver/NanoVNASaver.py | 5 +- 3 files changed, 175 insertions(+), 1 deletion(-) create mode 100644 NanoVNASaver/Charts/RIMu.py diff --git a/NanoVNASaver/Charts/RIMu.py b/NanoVNASaver/Charts/RIMu.py new file mode 100644 index 00000000..40952fd9 --- /dev/null +++ b/NanoVNASaver/Charts/RIMu.py @@ -0,0 +1,170 @@ +# 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 math +import numpy as np +import logging +from scipy.constants import mu_0 + +from PyQt5 import QtWidgets, QtGui + +from NanoVNASaver.Formatting import format_frequency_chart +from NanoVNASaver.RFTools import Datapoint +from NanoVNASaver.Charts.Chart import Chart + +from .RI import RealImaginaryChart + +logger = logging.getLogger(__name__) + +MU = "\N{GREEK SMALL LETTER MU}" + +class RealImaginaryMuChart(RealImaginaryChart): + + def __init__(self, name=""): + super().__init__(name) + self.y_menu.addSeparator() + + self.action_set_fixed_maximum_real = QtWidgets.QAction( + f"Maximum {MU}' ({self.maxDisplayReal})") + self.action_set_fixed_maximum_real.triggered.connect( + self.setMaximumRealValue) + + self.action_set_fixed_minimum_real = QtWidgets.QAction( + f"Minimum {MU}' ({self.minDisplayReal})") + self.action_set_fixed_minimum_real.triggered.connect( + self.setMinimumRealValue) + + self.action_set_fixed_maximum_imag = QtWidgets.QAction( + f"Maximum {MU}'' ({self.maxDisplayImag})") + self.action_set_fixed_maximum_imag.triggered.connect( + self.setMaximumImagValue) + + self.action_set_fixed_minimum_imag = QtWidgets.QAction( + f"Minimum {MU}'' ({self.minDisplayImag})") + self.action_set_fixed_minimum_imag.triggered.connect( + self.setMinimumImagValue) + + self.y_menu.addAction(self.action_set_fixed_maximum_real) + self.y_menu.addAction(self.action_set_fixed_minimum_real) + self.y_menu.addSeparator() + self.y_menu.addAction(self.action_set_fixed_maximum_imag) + self.y_menu.addAction(self.action_set_fixed_minimum_imag) + + # Manage core parameters + # TODO pick some sane default values? + self.coreLength = 1. + self.coreArea = 1. + self.coreWindings = 1 + + self.y_menu.addSeparator() + self.action_set_core_length = QtWidgets.QAction( + "Core effective length") + self.action_set_core_length.triggered.connect( + self.setCoreLength) + + self.y_menu.addSeparator() + self.action_set_core_area = QtWidgets.QAction( + "Core area") + self.action_set_core_area.triggered.connect( + self.setCoreArea) + + self.y_menu.addSeparator() + self.action_set_core_windings = QtWidgets.QAction( + "Core number of windings") + self.action_set_core_windings.triggered.connect( + self.setCoreWindings) + + self.y_menu.addAction(self.action_set_core_length) + self.y_menu.addAction(self.action_set_core_area) + self.y_menu.addAction(self.action_set_core_windings) + + def drawChart(self, qp: QtGui.QPainter): + qp.setPen(QtGui.QPen(Chart.color.text)) + qp.drawText(self.leftMargin + 5, 15, + f"{self.name}") + qp.drawText(5, 15, f"{MU}'") + qp.drawText(self.leftMargin + self.dim.width + 10, 15, f"{MU}''") + qp.setPen(QtGui.QPen(Chart.color.foreground)) + qp.drawLine(self.leftMargin, + self.topMargin - 5, + self.leftMargin, + self.topMargin + self.dim.height + 5) + qp.drawLine(self.leftMargin-5, + self.topMargin + self.dim.height, + self.leftMargin + self.dim.width + 5, + self.topMargin + self.dim.height) + self.drawTitle(qp) + + def contextMenuEvent(self, event): + self.action_set_fixed_start.setText( + f"Start ({format_frequency_chart(self.minFrequency)})") + self.action_set_fixed_stop.setText( + f"Stop ({format_frequency_chart(self.maxFrequency)})") + self.action_set_fixed_minimum_real.setText( + f"Minimum {MU}' ({self.minDisplayReal})") + self.action_set_fixed_maximum_real.setText( + f"Maximum {MU}' ({self.maxDisplayReal})") + self.action_set_fixed_minimum_imag.setText( + f"Minimum {MU}'' ({self.minDisplayImag})") + self.action_set_fixed_maximum_imag.setText( + f"Maximum {MU}'' ({self.maxDisplayImag})") + self.menu.exec_(event.globalPos()) + + def setCoreLength(self): + val, selected = QtWidgets.QInputDialog.getDouble( + self, "Core effective length", + "Set core effective length in mm", value=self.coreLength, + decimals=2) + if not selected: + return + if not (self.fixedValues and val >= 0): + self.coreLength = val + if self.fixedValues: + self.update() + + def setCoreArea(self): + val, selected = QtWidgets.QInputDialog.getDouble( + self, "Core effective area", + "Set core cross section area length in mm\N{SUPERSCRIPT TWO}", value=self.coreArea, + decimals=2) + if not selected: + return + if not (self.fixedValues and val >= 0): + self.coreArea = val + if self.fixedValues: + self.update() + + def setCoreWindings(self): + val, selected = QtWidgets.QInputDialog.getInt( + self, "Core number of windings", + "Set core number of windings", value=self.coreWindings) + if not selected: + return + if not (self.fixedValues and val >= 0): + self.coreWindings = val + if self.fixedValues: + self.update() + + def value(self, p: Datapoint) -> complex: + return self.mu_r(p) + + def mu_r(self, p: Datapoint) -> complex: + inductance = np.conj(-1j * p.impedance()/(2*math.pi*p.freq)) + + # Core length and core area are in mm and mm2 respectively + return inductance * (self.coreLength/1e3) / (mu_0 * self.coreWindings**2 * (self.coreArea/1e6)) diff --git a/NanoVNASaver/Charts/__init__.py b/NanoVNASaver/Charts/__init__.py index 30c55207..087d7cac 100644 --- a/NanoVNASaver/Charts/__init__.py +++ b/NanoVNASaver/Charts/__init__.py @@ -15,6 +15,7 @@ from .Phase import PhaseChart from .QFactor import QualityFactorChart from .RI import RealImaginaryChart +from .RIMu import RealImaginaryMuChart from .RIZ import RealImaginaryZChart from .RIZShunt import RealImaginaryZShuntChart from .RIZSeries import RealImaginaryZSeriesChart diff --git a/NanoVNASaver/NanoVNASaver.py b/NanoVNASaver/NanoVNASaver.py index 796e91ff..576d68b4 100644 --- a/NanoVNASaver/NanoVNASaver.py +++ b/NanoVNASaver/NanoVNASaver.py @@ -45,7 +45,9 @@ MagnitudeChart, MagnitudeZChart, MagnitudeZShuntChart, MagnitudeZSeriesChart, QualityFactorChart, VSWRChart, PermeabilityChart, PolarChart, - RealImaginaryChart, RealImaginaryZChart, RealImaginaryZShuntChart, RealImaginaryZSeriesChart, + RealImaginaryChart, + RealImaginaryMuChart, + RealImaginaryZChart, RealImaginaryZShuntChart, RealImaginaryZSeriesChart, SmithChart, SParameterChart, TDRChart, ) from .Calibration import Calibration @@ -152,6 +154,7 @@ def __init__(self): "phase": PhaseChart("S11 Phase"), "q_factor": QualityFactorChart("S11 Quality Factor"), "real_imag": RealImaginaryZChart("S11 R+jX"), + "real_imag_mu": RealImaginaryMuChart("S11 \N{GREEK SMALL LETTER MU}"), "smith": SmithChart("S11 Smith Chart"), "s_parameter": SParameterChart("S11 Real/Imaginary"), "vswr": VSWRChart("S11 VSWR"), From bc1ed91e96f1278fa34147c4a06a86ef3f2a09c3 Mon Sep 17 00:00:00 2001 From: Roel Jordans Date: Fri, 10 Feb 2023 18:56:27 +0100 Subject: [PATCH 3/6] Moved core dimension fields to toplevel menu also added copy method for new settings --- NanoVNASaver/Charts/RIMu.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/NanoVNASaver/Charts/RIMu.py b/NanoVNASaver/Charts/RIMu.py index 40952fd9..df29af9e 100644 --- a/NanoVNASaver/Charts/RIMu.py +++ b/NanoVNASaver/Charts/RIMu.py @@ -71,27 +71,34 @@ def __init__(self, name=""): self.coreArea = 1. self.coreWindings = 1 - self.y_menu.addSeparator() + self.menu.addSeparator() self.action_set_core_length = QtWidgets.QAction( "Core effective length") self.action_set_core_length.triggered.connect( self.setCoreLength) - self.y_menu.addSeparator() self.action_set_core_area = QtWidgets.QAction( "Core area") self.action_set_core_area.triggered.connect( self.setCoreArea) - self.y_menu.addSeparator() self.action_set_core_windings = QtWidgets.QAction( "Core number of windings") self.action_set_core_windings.triggered.connect( self.setCoreWindings) - self.y_menu.addAction(self.action_set_core_length) - self.y_menu.addAction(self.action_set_core_area) - self.y_menu.addAction(self.action_set_core_windings) + self.menu.addAction(self.action_set_core_length) + self.menu.addAction(self.action_set_core_area) + self.menu.addAction(self.action_set_core_windings) + + def copy(self): + new_chart: RealImaginaryMuChart = super().copy() + + new_chart.coreLength = self.coreLength + new_chart.coreArea = self.coreArea + new_chart.coreWindings = self.coreWindings + + return new_chart def drawChart(self, qp: QtGui.QPainter): qp.setPen(QtGui.QPen(Chart.color.text)) From 59b3e8a1ef1645c2cdeddf5f60a2231c7df0c522 Mon Sep 17 00:00:00 2001 From: Roel Jordans Date: Sat, 11 Feb 2023 14:04:58 +0100 Subject: [PATCH 4/6] Clarify calculation and origin of np.conj() --- NanoVNASaver/Charts/RIMu.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/NanoVNASaver/Charts/RIMu.py b/NanoVNASaver/Charts/RIMu.py index df29af9e..d35609bd 100644 --- a/NanoVNASaver/Charts/RIMu.py +++ b/NanoVNASaver/Charts/RIMu.py @@ -171,7 +171,8 @@ def value(self, p: Datapoint) -> complex: return self.mu_r(p) def mu_r(self, p: Datapoint) -> complex: - inductance = np.conj(-1j * p.impedance()/(2*math.pi*p.freq)) + inductance = p.impedance()/(2j*math.pi*p.freq) # Core length and core area are in mm and mm2 respectively - return inductance * (self.coreLength/1e3) / (mu_0 * self.coreWindings**2 * (self.coreArea/1e6)) + # note: mu_r = mu' - j * mu '' + return np.conj(inductance * (self.coreLength/1e3) / (mu_0 * self.coreWindings**2 * (self.coreArea/1e6))) From 0cb7b4ee6d182829c608baee8e7cdf7bb3b9f4b3 Mon Sep 17 00:00:00 2001 From: Roel Jordans Date: Sun, 12 Feb 2023 11:37:00 +0100 Subject: [PATCH 5/6] Added brief documentation of the new S11 mu option to README.md Signed-off-by: Roel Jordans --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index eede1b31..0cc5136a 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,7 @@ It's written in __Python 3__ using __PyQt5__ and __scipy__. - [Usage](#using-the-software) - [Calibration](#calibration) - [TDR](#tdr) + - [Measuring inductor core permeability](#measuring-inductor-core-permeability) - [Latest Changes](#latest-changes) - [Contributing](#contributing) - [Contribution Guidlines](docs/CONTRIBUTING.md) @@ -173,6 +174,10 @@ calibration load would be attached. Open the "Time Domain Reflectometry" window, and select the correct cable type, or manually enter a propagation factor. +### Measuring inductor core permeability + +The permeability (mu) of cores can be measured using a one-port measurement. Put one or more windings on a core of known dimensions and use the "S11 mu" plot from the "Display Setup". The core dimensions (cross section area in mm2, effective length in mm) and number of windings can be set in the context menu for the plot (right click on the plot). + Latest Changes -------------- From 8586cfc0a850fa9b85b08687bf0c50fc51250553 Mon Sep 17 00:00:00 2001 From: Roel Jordans Date: Sun, 12 Feb 2023 16:48:47 +0100 Subject: [PATCH 6/6] Stop sweeping with stop button in continous mode --- NanoVNASaver/SweepWorker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NanoVNASaver/SweepWorker.py b/NanoVNASaver/SweepWorker.py index d43e3464..8f3e531a 100644 --- a/NanoVNASaver/SweepWorker.py +++ b/NanoVNASaver/SweepWorker.py @@ -134,7 +134,7 @@ def _run_loop(self) -> None: start, stop, averages) self.percentage = (i + 1) * 100 / sweep.segments self.updateData(freq, values11, values21, i) - if sweep.properties.mode != SweepMode.CONTINOUS: + if sweep.properties.mode != SweepMode.CONTINOUS or self.stopped: break def init_data(self):