diff --git a/NanoVNASaver/Chart.py b/NanoVNASaver/Chart.py index f34d0d7..fedf50c 100644 --- a/NanoVNASaver/Chart.py +++ b/NanoVNASaver/Chart.py @@ -481,6 +481,10 @@ class PhaseChart(FrequencyChart): self.minAngle = 0 self.maxAngle = 0 self.span = 0 + self.unwrap = False + + self.unwrappedData = [] + self.unwrappedReference = [] self.minDisplayValue = -180 self.maxDisplayValue = 180 @@ -492,34 +496,22 @@ class PhaseChart(FrequencyChart): self.setPalette(pal) self.setAutoFillBackground(True) + self.y_menu.addSeparator() + self.action_unwrap = QtWidgets.QAction("Unwrap") + self.action_unwrap.setCheckable(True) + self.action_unwrap.triggered.connect(lambda: self.setUnwrap(self.action_unwrap.isChecked())) + self.y_menu.addAction(self.action_unwrap) + + def setUnwrap(self, unwrap: bool): + self.unwrap = unwrap + self.update() + def drawChart(self, qp: QtGui.QPainter): qp.setPen(QtGui.QPen(self.textColor)) qp.drawText(3, 15, self.name) qp.setPen(QtGui.QPen(self.foregroundColor)) qp.drawLine(self.leftMargin, 20, self.leftMargin, self.topMargin+self.chartHeight+5) qp.drawLine(self.leftMargin-5, self.topMargin+self.chartHeight, self.leftMargin+self.chartWidth, self.topMargin + self.chartHeight) - if self.fixedValues: - minAngle = self.minDisplayValue - maxAngle = self.maxDisplayValue - else: - minAngle = -180 - maxAngle = 180 - span = maxAngle-minAngle - self.minAngle = minAngle - self.maxAngle = maxAngle - self.span = span - step = math.floor(span/4) - for i in range(minAngle, maxAngle, step): - y = self.topMargin + round((maxAngle - i)/span*self.chartHeight) - if i != minAngle and i != maxAngle and (maxAngle - i) > step / 2: - qp.setPen(QtGui.QPen(self.textColor)) - qp.drawText(3, y+3, str(i) + "°") - qp.setPen(QtGui.QPen(self.foregroundColor)) - qp.drawLine(self.leftMargin - 5, y, self.leftMargin + self.chartWidth, y) - qp.drawLine(self.leftMargin - 5, self.topMargin, self.leftMargin + self.chartWidth, self.topMargin) - qp.setPen(self.textColor) - qp.drawText(3, 35, str(maxAngle) + "°") - qp.drawText(3, self.chartHeight+self.topMargin, str(minAngle) + "°") def drawValues(self, qp: QtGui.QPainter): if len(self.data) == 0 and len(self.reference) == 0: @@ -529,6 +521,50 @@ class PhaseChart(FrequencyChart): line_pen = QtGui.QPen(self.sweepColor) line_pen.setWidth(1) + if self.unwrap: + rawData = [] + for d in self.data: + rawData.append(self.angle(d)) + + rawReference = [] + for d in self.reference: + rawReference.append(self.angle(d)) + + self.unwrappedData = np.unwrap(rawData, 180) + self.unwrappedReference = np.unwrap(rawReference, 180) + + if self.fixedValues: + minAngle = self.minDisplayValue + maxAngle = self.maxDisplayValue + elif self.unwrap and self.data: + minAngle = math.floor(np.min(self.unwrappedData)) + maxAngle = math.ceil(np.max(self.unwrappedData)) + elif self.unwrap and self.reference: + minAngle = math.floor(np.min(self.unwrappedReference)) + maxAngle = math.ceil(np.max(self.unwrappedReference)) + else: + minAngle = -180 + maxAngle = 180 + + span = maxAngle - minAngle + self.minAngle = minAngle + self.maxAngle = maxAngle + self.span = span + step = math.floor(span / 4) + if step == 0: + step = 1 + for i in range(minAngle, maxAngle, step): + y = self.topMargin + round((maxAngle - i) / span * self.chartHeight) + if i != minAngle and i != maxAngle and (maxAngle - i) > step / 2: + qp.setPen(QtGui.QPen(self.textColor)) + qp.drawText(3, y + 3, str(i) + "°") + qp.setPen(QtGui.QPen(self.foregroundColor)) + qp.drawLine(self.leftMargin - 5, y, self.leftMargin + self.chartWidth, y) + qp.drawLine(self.leftMargin - 5, self.topMargin, self.leftMargin + self.chartWidth, self.topMargin) + qp.setPen(self.textColor) + qp.drawText(3, 35, str(maxAngle) + "°") + qp.drawText(3, self.chartHeight + self.topMargin, str(minAngle) + "°") + if self.fixedSpan: fstart = self.minFrequency fstop = self.maxFrequency @@ -562,7 +598,15 @@ class PhaseChart(FrequencyChart): self.drawMarkers(qp) def getYPosition(self, d: Datapoint) -> int: - angle = self.angle(d) + if self.unwrap: + if d in self.data: + angle = self.unwrappedData[self.data.index(d)] + elif d in self.reference: + angle = self.unwrappedReference[self.reference.index(d)] + else: + angle = self.angle(d) + else: + angle = self.angle(d) return 30 + round((self.maxAngle - angle) / self.span * self.chartHeight) @staticmethod diff --git a/NanoVNASaver/NanoVNASaver.py b/NanoVNASaver/NanoVNASaver.py index b60d42c..e6d6e9f 100644 --- a/NanoVNASaver/NanoVNASaver.py +++ b/NanoVNASaver/NanoVNASaver.py @@ -207,7 +207,7 @@ class NanoVNASaver(QtWidgets.QWidget): sweep_control_layout.addRow(QtWidgets.QLabel("Segments"), segment_layout) self.sweepSettingsWindow = SweepSettingsWindow(self) - btn_sweep_settings_window = QtWidgets.QPushButton("Sweep settings") + btn_sweep_settings_window = QtWidgets.QPushButton("Sweep settings ...") btn_sweep_settings_window.clicked.connect(self.displaySweepSettingsWindow) sweep_control_layout.addRow(btn_sweep_settings_window) @@ -1562,31 +1562,90 @@ class SweepSettingsWindow(QtWidgets.QWidget): shortcut = QtWidgets.QShortcut(QtCore.Qt.Key_Escape, self, self.hide) - layout = QtWidgets.QFormLayout() + layout = QtWidgets.QVBoxLayout() self.setLayout(layout) + settings_box = QtWidgets.QGroupBox("Settings") + settings_layout = QtWidgets.QFormLayout(settings_box) + self.single_sweep_radiobutton = QtWidgets.QRadioButton("Single sweep") self.continuous_sweep_radiobutton = QtWidgets.QRadioButton("Continuous sweep") self.averaged_sweep_radiobutton = QtWidgets.QRadioButton("Averaged sweep") - layout.addWidget(self.single_sweep_radiobutton) + settings_layout.addWidget(self.single_sweep_radiobutton) self.single_sweep_radiobutton.setChecked(True) - layout.addWidget(self.continuous_sweep_radiobutton) - layout.addWidget(self.averaged_sweep_radiobutton) + settings_layout.addWidget(self.continuous_sweep_radiobutton) + settings_layout.addWidget(self.averaged_sweep_radiobutton) self.averages = QtWidgets.QLineEdit("3") self.truncates = QtWidgets.QLineEdit("0") - layout.addRow("Number of measurements to average", self.averages) - layout.addRow("Number to discard", self.truncates) - layout.addRow(QtWidgets.QLabel("Averaging allows discarding outlying samples to get better averages.")) - layout.addRow(QtWidgets.QLabel("Common values are 3/0, 5/2, 9/4 and 25/6.")) + settings_layout.addRow("Number of measurements to average", self.averages) + settings_layout.addRow("Number to discard", self.truncates) + settings_layout.addRow(QtWidgets.QLabel("Averaging allows discarding outlying samples to get better averages.")) + settings_layout.addRow(QtWidgets.QLabel("Common values are 3/0, 5/2, 9/4 and 25/6.")) self.continuous_sweep_radiobutton.toggled.connect(lambda: self.app.worker.setContinuousSweep(self.continuous_sweep_radiobutton.isChecked())) self.averaged_sweep_radiobutton.toggled.connect(self.updateAveraging) self.averages.textEdited.connect(self.updateAveraging) self.truncates.textEdited.connect(self.updateAveraging) + layout.addWidget(settings_box) + + band_sweep_box = QtWidgets.QGroupBox("Sweep band") + band_sweep_layout = QtWidgets.QFormLayout(band_sweep_box) + + self.band_list = QtWidgets.QComboBox() + self.band_list.setModel(self.app.bands) + self.band_list.currentIndexChanged.connect(self.updateCurrentBand) + + band_sweep_layout.addRow("Select band", self.band_list) + + self.band_pad_limits = QtWidgets.QCheckBox("Pad band limits (10%)") + self.band_pad_limits.stateChanged.connect(self.updateCurrentBand) + band_sweep_layout.addRow(self.band_pad_limits) + + self.band_limit_label = QtWidgets.QLabel() + + band_sweep_layout.addRow(self.band_limit_label) + + btn_set_band_sweep = QtWidgets.QPushButton("Set band sweep") + btn_set_band_sweep.clicked.connect(self.setBandSweep) + band_sweep_layout.addRow(btn_set_band_sweep) + + self.updateCurrentBand() + + layout.addWidget(band_sweep_box) + + def updateCurrentBand(self): + index_start = self.band_list.model().index(self.band_list.currentIndex(), 1) + index_stop = self.band_list.model().index(self.band_list.currentIndex(), 2) + start = int(self.band_list.model().data(index_start, QtCore.Qt.ItemDataRole).value()) + stop = int(self.band_list.model().data(index_stop, QtCore.Qt.ItemDataRole).value()) + + if self.band_pad_limits.isChecked(): + span = stop - start + start -= round(span / 10) + stop += round(span / 10) + + self.band_limit_label.setText("Sweep span: " + NanoVNASaver.formatShortFrequency(start) + " to " + + NanoVNASaver.formatShortFrequency(stop)) + + def setBandSweep(self): + index_start = self.band_list.model().index(self.band_list.currentIndex(), 1) + index_stop = self.band_list.model().index(self.band_list.currentIndex(), 2) + start = int(self.band_list.model().data(index_start, QtCore.Qt.ItemDataRole).value()) + stop = int(self.band_list.model().data(index_stop, QtCore.Qt.ItemDataRole).value()) + + if self.band_pad_limits.isChecked(): + span = stop - start + start -= round(span / 10) + stop += round(span / 10) + + self.app.sweepStartInput.setText(str(start)) + self.app.sweepEndInput.setText(str(stop)) + self.app.sweepEndInput.textEdited.emit(self.app.sweepEndInput.text()) + def updateAveraging(self): self.app.worker.setAveraging(self.averaged_sweep_radiobutton.isChecked(), self.averages.text(), diff --git a/NanoVNASaver/about.py b/NanoVNASaver/about.py index e89face..6ce410a 100644 --- a/NanoVNASaver/about.py +++ b/NanoVNASaver/about.py @@ -14,5 +14,5 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -version = '0.1.0' -debug = False +version = '0.1.1alpha' +debug = True