Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v0.3.9 #400

Merged
merged 36 commits into from
Jun 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
2052718
Refactoring
Dec 19, 2020
d06bb32
some fix ?!?
Dec 20, 2020
e95fea4
add some analysis
Dec 20, 2020
db284fd
some tolerance
Dec 20, 2020
7feff63
simple fix markers
Dec 21, 2020
922f243
both Simple peak an fixed peak
Dec 21, 2020
2f2c0d6
compare previus analysis
Dec 21, 2020
391b888
compares even if different number of resonances
Dec 22, 2020
9c1031f
show diff
Dec 22, 2020
06912da
fix typo
Dec 23, 2020
ec08d06
try to read initial frequencies
Dec 23, 2020
654252e
better compare ?!?
Dec 23, 2020
69db5f1
show peaks
Dec 23, 2020
8ab2709
ensure sorting
Dec 24, 2020
45294a1
v0.3.9-pre
zarath Dec 31, 2020
90e4dc9
Merge pull request #357 from gaionim/features/crossingZero
zarath Dec 31, 2020
320a61f
Merge pull request #359 from gaionim/fix/352
zarath Dec 31, 2020
79ef110
Merge pull request #360 from gaionim/features/readFreq
zarath Dec 31, 2020
5fa16c6
401 sweep points
galileo-pkm Jan 8, 2021
c6878fc
Added measurement graphs showing impedance measured via S21
rjordans Feb 18, 2021
911fb23
Merge pull request #376 from rjordans/master
zarath Feb 19, 2021
4204bad
Merge pull request #369 from galileo-pkm/H4_401
zarath Feb 19, 2021
34673a1
Merge pull request #372 from NanoVNA-Saver/Development
zarath Feb 19, 2021
9c7f6a8
Adding support for plotting |Z| on logarithmic scale
rjordans Feb 21, 2021
60ea55b
Cleaning up code duplication for log scale plotting
rjordans Feb 21, 2021
dbe735a
Fancy formatting (mkM) for impedance plots (|Z| and R+jX)
rjordans Feb 21, 2021
ba63ef5
Merge pull request #377 from rjordans/master
zarath Mar 19, 2021
dd8e865
Hardware: Add information about maximum stop frequency for all models
silbe Apr 3, 2021
a4f2956
Add support for setting TX power on NanoVNA v2
silbe Apr 6, 2021
9e85730
Fixing minimum axis label value in RI charts
rjordans Apr 23, 2021
b306854
Add new s21 markers for |Z| and R+jX
akinad3 Apr 4, 2021
252d0d2
Merge pull request #387 from silbe/nanovna-v2-tx-power
zarath May 7, 2021
c821fb2
Merge pull request #389 from rjordans/testing
zarath May 8, 2021
6be2730
Merge pull request #384 from akinad3/update_markers
zarath May 8, 2021
28c62b7
v0.3.9-rc01
zarath May 19, 2021
0c352ee
v0.3.9
zarath Jun 20, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Default for all text files
* text=auto whitespace=trailing-space,tab-in-indent,tabwidth=2
*.py text=auto whitespace=trailing-space,tab-in-indent,tabwidth=4

# Denote all files that are truly binary and should not be modified.
*.png binary
*.jpg binary
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@
settings.json
.gitignore
.coverage
/nanovna-saver.exe.spec
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
Changelog
=========

v0.3.9
------

- TX Power on V2
- New analysis
- Magnitude Z Chart
- VSWR Chart improvements

v0.3.8
------

Expand Down
2 changes: 1 addition & 1 deletion NanoVNASaver/About.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.

VERSION = "0.3.8"
VERSION = "0.3.9"
VERSION_URL = (
"https://raw.githubusercontent.com/"
"NanoVNA-Saver/nanovna-saver/master/NanoVNASaver/About.py")
Expand Down
102 changes: 99 additions & 3 deletions NanoVNASaver/Analysis/Analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,109 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
import logging
import math

import numpy as np
from scipy.signal import argrelextrema
from PyQt5 import QtWidgets
from scipy import signal

logger = logging.getLogger(__name__)


class Analysis:
_widget = None

@classmethod
def find_crossing_zero(cls, data, threshold=0):
'''

Find values crossing zero
return list of tuples (before, crossing, after)
indicating the index of data list
crossing is where data == 0
or data nearest 0

at maximum 1 value == 0
data must not start or end with 0


:param cls:
:param data: list of values
:param threshold: unused, for future manage flipping around 0
'''
my_data = np.array(data)
zeroes = np.where(my_data == 0)[0]

if 0 in zeroes:
raise ValueError("Data must non start with 0")

if len(data) - 1 in zeroes:
raise ValueError("Data must non end with 0")
crossing = [(n - 1, n, n + 1) for n in zeroes]

for n in np.where((my_data[:-1] * my_data[1:]) < 0)[0]:
if abs(data[n]) <= abs(data[n + 1]):
crossing.append((n, n, n + 1))
else:
crossing.append((n, n + 1, n + 1))

return crossing

@classmethod
def find_minimums(cls, data, threshold):
'''

Find values above threshold
return list of tuples (start, lowest, end)
indicating the index of data list


:param cls:
:param data: list of values
:param threshold:
'''

minimums = []
min_start = -1
min_idx = -1

min_val = threshold
for i, d in enumerate(data):
if d < threshold and i < len(data) - 1:
if d < min_val:
min_val = d
min_idx = i
if min_start == -1:
min_start = i
elif min_start != -1:
# We are above the threshold, and were in a section that was
# below
minimums.append((min_start, min_idx, i - 1))
min_start = -1
min_idx = -1
min_val = threshold
return minimums

@classmethod
def find_maximums(cls, data, threshold=None):
'''

Find peacs


:param cls:
:param data: list of values
:param threshold:
'''
peaks, _ = signal.find_peaks(
data, width=2, distance=3, prominence=1)

# my_data = np.array(data)
# maximums = argrelextrema(my_data, np.greater)[0]
if threshold is None:
return peaks
else:
return [k for k in peaks if data[k] > threshold]

def __init__(self, app: QtWidgets.QWidget):
self.app = app

Expand All @@ -50,8 +144,10 @@ def calculateRolloff(self, location1, location2):
if frequency_factor < 1:
frequency_factor = 1 / frequency_factor
attenuation = abs(gain1 - gain2)
logger.debug("Measured points: %d Hz and %d Hz", frequency1, frequency2)
logger.debug("Measured points: %d Hz and %d Hz",
frequency1, frequency2)
logger.debug("%f dB over %f factor", attenuation, frequency_factor)
octave_attenuation = attenuation / (math.log10(frequency_factor) / math.log10(2))
octave_attenuation = attenuation / \
(math.log10(frequency_factor) / math.log10(2))
decade_attenuation = attenuation / math.log10(frequency_factor)
return octave_attenuation, decade_attenuation
82 changes: 56 additions & 26 deletions NanoVNASaver/Analysis/PeakSearchAnalysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@
import numpy as np

from NanoVNASaver.Analysis import Analysis
from NanoVNASaver.Formatting import format_vswr
from NanoVNASaver.Formatting import format_gain
from NanoVNASaver.Formatting import format_resistance
from NanoVNASaver.Formatting import format_frequency_short


logger = logging.getLogger(__name__)
Expand All @@ -38,8 +42,8 @@ def __init__(self, app):
super().__init__(app)

self._widget = QtWidgets.QWidget()
outer_layout = QtWidgets.QFormLayout()
self._widget.setLayout(outer_layout)
self.layout = QtWidgets.QFormLayout()
self._widget.setLayout(self.layout)

self.rbtn_data_group = QtWidgets.QButtonGroup()
self.rbtn_data_vswr = QtWidgets.QRadioButton("VSWR")
Expand Down Expand Up @@ -70,40 +74,56 @@ def __init__(self, app):

self.checkbox_move_markers = QtWidgets.QCheckBox()

outer_layout.addRow(QtWidgets.QLabel("<b>Settings</b>"))
outer_layout.addRow("Data source", self.rbtn_data_vswr)
outer_layout.addRow("", self.rbtn_data_resistance)
outer_layout.addRow("", self.rbtn_data_reactance)
outer_layout.addRow("", self.rbtn_data_s21_gain)
outer_layout.addRow(PeakSearchAnalysis.QHLine())
outer_layout.addRow("Peak type", self.rbtn_peak_positive)
outer_layout.addRow("", self.rbtn_peak_negative)
self.layout.addRow(QtWidgets.QLabel("<b>Settings</b>"))
self.layout.addRow("Data source", self.rbtn_data_vswr)
self.layout.addRow("", self.rbtn_data_resistance)
self.layout.addRow("", self.rbtn_data_reactance)
self.layout.addRow("", self.rbtn_data_s21_gain)
self.layout.addRow(PeakSearchAnalysis.QHLine())
self.layout.addRow("Peak type", self.rbtn_peak_positive)
self.layout.addRow("", self.rbtn_peak_negative)
# outer_layout.addRow("", self.rbtn_peak_both)
outer_layout.addRow(PeakSearchAnalysis.QHLine())
outer_layout.addRow("Max number of peaks", self.input_number_of_peaks)
outer_layout.addRow("Move markers", self.checkbox_move_markers)
outer_layout.addRow(PeakSearchAnalysis.QHLine())

outer_layout.addRow(QtWidgets.QLabel("<b>Results</b>"))
self.layout.addRow(PeakSearchAnalysis.QHLine())
self.layout.addRow("Max number of peaks", self.input_number_of_peaks)
self.layout.addRow("Move markers", self.checkbox_move_markers)
self.layout.addRow(PeakSearchAnalysis.QHLine())
self.layout.addRow(QtWidgets.QLabel("<b>Results</b>"))
self.results_header = self.layout.rowCount()

def runAnalysis(self):
self.reset()
data = []
sign = 1
count = self.input_number_of_peaks.value()
if self.rbtn_data_vswr.isChecked():
data = []
fn = format_vswr
for d in self.app.data11:
data11.append(d.vswr)
data.append(d.vswr)
elif self.rbtn_data_s21_gain.isChecked():
data = []
fn = format_gain
for d in self.app.data21:
data.append(d.gain)
elif self.rbtn_data_resistance.isChecked():
fn = format_resistance
for d in self.app.data11:
data.append(d.impedance().real)
elif self.rbtn_data_reactance.isChecked():
fn = str
for d in self.app.data11:
data.append(d.impedance().imag)

else:
logger.warning("Searching for peaks on unknown data")
return

if self.rbtn_peak_positive.isChecked():
peaks, _ = signal.find_peaks(data, width=3, distance=3, prominence=1)
peaks, _ = signal.find_peaks(
data, width=3, distance=3, prominence=1)
elif self.rbtn_peak_negative.isChecked():
peaks, _ = signal.find_peaks(np.array(data)*-1, width=3, distance=3, prominence=1)
sign = -1
data = [x * sign for x in data]
peaks, _ = signal.find_peaks(
data, width=3, distance=3, prominence=1)
# elif self.rbtn_peak_both.isChecked():
# peaks_max, _ = signal.find_peaks(data, width=3, distance=3, prominence=1)
# peaks_min, _ = signal.find_peaks(np.array(data)*-1, width=3, distance=3, prominence=1)
Expand All @@ -117,8 +137,8 @@ def runAnalysis(self):

# Having found the peaks, get the prominence data

for p in peaks:
logger.debug("Peak at %d", p)
for i, p in np.ndenumerate(peaks):
logger.debug("Peak %i at %d", i, p)
prominences = signal.peak_prominences(data, peaks)[0]
logger.debug("%d prominences", len(prominences))

Expand All @@ -131,9 +151,13 @@ def runAnalysis(self):
logger.debug("Prominence %f", prominences[i])
logger.debug("Index in sweep %d", peaks[i])
logger.debug("Frequency %d", self.app.data11[peaks[i]].freq)
logger.debug("Value %f", data[peaks[i]])
logger.debug("Value %f", sign * data[peaks[i]])
self.layout.addRow(
f"Freq {format_frequency_short(self.app.data11[peaks[i]].freq)}",
QtWidgets.QLabel(f" value {fn(sign * data[peaks[i]])}"
))

if self.checkbox_move_markers:
if self.checkbox_move_markers.isChecked():
if count > len(self.app.markers):
logger.warning("More peaks found than there are markers")
for i in range(min(count, len(self.app.markers))):
Expand All @@ -152,4 +176,10 @@ def runAnalysis(self):
logger.debug("Max peak at %d, value %f", max_idx, max_val)

def reset(self):
pass
logger.debug("Reset analysis")

logger.debug("Results start at %d, out of %d",
self.results_header, self.layout.rowCount())
for i in range(self.results_header, self.layout.rowCount()):
logger.debug("deleting %s", self.layout.rowCount())
self.layout.removeRow(self.layout.rowCount() - 1)
Loading