Skip to content

Commit

Permalink
Added measurement graphs showing impedance measured via S21
Browse files Browse the repository at this point in the history
Fixes #165

- Allows for both |Z| and R + jX display
- Measurements of shunt impedance (accurate for low impedance values)
  as well as series impedance (accurate for high impedance values)
  • Loading branch information
rjordans committed Feb 19, 2021
1 parent 97c7c8f commit c6878fc
Show file tree
Hide file tree
Showing 9 changed files with 195 additions and 8 deletions.
9 changes: 8 additions & 1 deletion NanoVNASaver/Charts/MagnitudeZ.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ def drawValues(self, qp: QtGui.QPainter):
maxValue = 0
for d in self.data:
mag = self.magnitude(d)
if math.isinf(mag): # Avoid infinite scales
continue
if mag > maxValue:
maxValue = mag
if mag < minValue:
Expand All @@ -97,6 +99,8 @@ def drawValues(self, qp: QtGui.QPainter):
if d.freq < self.fstart or d.freq > self.fstop:
continue
mag = self.magnitude(d)
if math.isinf(mag): # Avoid infinite scales
continue
if mag > maxValue:
maxValue = mag
if mag < minValue:
Expand Down Expand Up @@ -142,7 +146,10 @@ def drawValues(self, qp: QtGui.QPainter):

def getYPosition(self, d: Datapoint) -> int:
mag = self.magnitude(d)
return self.topMargin + round((self.maxValue - mag) / self.span * self.chartHeight)
if math.isfinite(mag):
return self.topMargin + round((self.maxValue - mag) / self.span * self.chartHeight)
else:
return self.topMargin

def valueAtPosition(self, y) -> List[float]:
absy = y - self.topMargin
Expand Down
40 changes: 40 additions & 0 deletions NanoVNASaver/Charts/MagnitudeZSeries.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@

# NanoVNASaver
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
# Copyright (C) 2020 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 math
import logging
from typing import List

from PyQt5 import QtWidgets, QtGui

from NanoVNASaver.RFTools import Datapoint
from .MagnitudeZ import MagnitudeZChart


logger = logging.getLogger(__name__)


class MagnitudeZSeriesChart(MagnitudeZChart):
def __init__(self, name=""):
super().__init__(name)

@staticmethod
def magnitude(p: Datapoint) -> float:
return abs(p.seriesImpedance())

40 changes: 40 additions & 0 deletions NanoVNASaver/Charts/MagnitudeZShunt.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@

# NanoVNASaver
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
# Copyright (C) 2020 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 math
import logging
from typing import List

from PyQt5 import QtWidgets, QtGui

from NanoVNASaver.RFTools import Datapoint
from .MagnitudeZ import MagnitudeZChart


logger = logging.getLogger(__name__)


class MagnitudeZShuntChart(MagnitudeZChart):
def __init__(self, name=""):
super().__init__(name)

@staticmethod
def magnitude(p: Datapoint) -> float:
return abs(p.shuntImpedance())

20 changes: 15 additions & 5 deletions NanoVNASaver/Charts/RI.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,8 +178,10 @@ def drawValues(self, qp: QtGui.QPainter):
max_real = 0
max_imag = -1000
for d in self.data:
imp = d.impedance()
imp = self.impedance(d)
re, im = imp.real, imp.imag
if math.isinf(re): # Avoid infinite scales
continue
if re > max_real:
max_real = re
if re < min_real:
Expand All @@ -191,8 +193,10 @@ def drawValues(self, qp: QtGui.QPainter):
for d in self.reference: # Also check min/max for the reference sweep
if d.freq < fstart or d.freq > fstop:
continue
imp = d.impedance()
imp = self.impedance(d)
re, im = imp.real, imp.imag
if math.isinf(re): # Avoid infinite scales
continue
if re > max_real:
max_real = re
if re < min_real:
Expand Down Expand Up @@ -397,12 +401,15 @@ def drawValues(self, qp: QtGui.QPainter):
self.drawMarker(x, y_im, qp, m.color, self.markers.index(m)+1)

def getImYPosition(self, d: Datapoint) -> int:
im = d.impedance().imag
im = self.impedance(d).imag
return self.topMargin + round((self.max_imag - im) / self.span_imag * self.chartHeight)

def getReYPosition(self, d: Datapoint) -> int:
re = d.impedance().real
return self.topMargin + round((self.max_real - re) / self.span_real * self.chartHeight)
re = self.impedance(d).real
if math.isfinite(re):
return self.topMargin + round((self.max_real - re) / self.span_real * self.chartHeight)
else:
return self.topMargin

def valueAtPosition(self, y) -> List[float]:
absy = y - self.topMargin
Expand Down Expand Up @@ -520,3 +527,6 @@ def contextMenuEvent(self, event):
self.action_set_fixed_maximum_imag.setText(
f"Maximum jX ({self.maxDisplayImag})")
self.menu.exec_(event.globalPos())

def impedance(self, p: Datapoint) -> complex:
return p.impedance()
35 changes: 35 additions & 0 deletions NanoVNASaver/Charts/RISeries.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# NanoVNASaver
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
# Copyright (C) 2020 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 math
import logging
from typing import List

from NanoVNASaver.RFTools import Datapoint

from .RI import RealImaginaryChart

logger = logging.getLogger(__name__)


class RealImaginarySeriesChart(RealImaginaryChart):
def __init__(self, name=""):
super().__init__(name)

def impedance(self, p: Datapoint) -> complex:
return p.seriesImpedance()
35 changes: 35 additions & 0 deletions NanoVNASaver/Charts/RIShunt.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# NanoVNASaver
#
# A python program to view and export Touchstone data from a NanoVNA
# Copyright (C) 2019, 2020 Rune B. Broberg
# Copyright (C) 2020 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 math
import logging
from typing import List

from NanoVNASaver.RFTools import Datapoint

from .RI import RealImaginaryChart

logger = logging.getLogger(__name__)


class RealImaginaryShuntChart(RealImaginaryChart):
def __init__(self, name=""):
super().__init__(name)

def impedance(self, p: Datapoint) -> complex:
return p.shuntImpedance()
4 changes: 4 additions & 0 deletions NanoVNASaver/Charts/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,14 @@
from .CLogMag import CombinedLogMagChart
from .Magnitude import MagnitudeChart
from .MagnitudeZ import MagnitudeZChart
from .MagnitudeZShunt import MagnitudeZShuntChart
from .MagnitudeZSeries import MagnitudeZSeriesChart
from .Permeability import PermeabilityChart
from .Phase import PhaseChart
from .QFactor import QualityFactorChart
from .RI import RealImaginaryChart
from .RIShunt import RealImaginaryShuntChart
from .RISeries import RealImaginarySeriesChart
from .Smith import SmithChart
from .SParam import SParameterChart
from .TDR import TDRChart
Expand Down
8 changes: 6 additions & 2 deletions NanoVNASaver/NanoVNASaver.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@
CapacitanceChart,
CombinedLogMagChart, GroupDelayChart, InductanceChart,
LogMagChart, PhaseChart,
MagnitudeChart, MagnitudeZChart,
MagnitudeChart, MagnitudeZChart, MagnitudeZShuntChart, MagnitudeZSeriesChart,
QualityFactorChart, VSWRChart, PermeabilityChart, PolarChart,
RealImaginaryChart,
RealImaginaryChart, RealImaginaryShuntChart, RealImaginarySeriesChart,
SmithChart, SParameterChart, TDRChart,
)
from .Calibration import Calibration
Expand Down Expand Up @@ -155,6 +155,10 @@ def __init__(self):
reflective=False)),
("log_mag", LogMagChart("S21 Gain")),
("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("S21 R+jX series")),
("phase", PhaseChart("S21 Phase")),
("polar", PolarChart("S21 Polar Plot")),
("s_parameter", SParameterChart("S21 Real/Imaginary")),
Expand Down
12 changes: 12 additions & 0 deletions NanoVNASaver/RFTools.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,18 @@ def wavelength(self) -> float:
def impedance(self, ref_impedance: float = 50) -> complex:
return gamma_to_impedance(self.z, ref_impedance)

def shuntImpedance(self, ref_impedance: float = 50) -> complex:
try:
return 0.5 * ref_impedance * self.z / (1 - self.z)
except ZeroDivisionError:
return math.inf

def seriesImpedance(self, ref_impedance: float = 50) -> complex:
try:
return 2 * ref_impedance * (1 - self.z) / self.z
except ZeroDivisionError:
return math.inf

def qFactor(self, ref_impedance: float = 50) -> float:
imp = self.impedance(ref_impedance)
if imp.real == 0.0:
Expand Down

0 comments on commit c6878fc

Please sign in to comment.