kopia lustrzana https://github.com/NanoVNA-Saver/nanovna-saver
				
				
				
			Refactoring Sweep settings
							rodzic
							
								
									bf8d5a4544
								
							
						
					
					
						commit
						684a01beb4
					
				|  | @ -18,7 +18,6 @@ | |||
| #  along with this program.  If not, see <https://www.gnu.org/licenses/>. | ||||
| import logging | ||||
| import struct | ||||
| from time import sleep, time | ||||
| from typing import List | ||||
| 
 | ||||
| import serial | ||||
|  | @ -26,7 +25,8 @@ import numpy as np | |||
| from PyQt5 import QtGui | ||||
| 
 | ||||
| from NanoVNASaver.Hardware.Serial import drain_serial, Interface | ||||
| from NanoVNASaver.Hardware.VNA import VNA, Version | ||||
| from NanoVNASaver.Hardware.VNA import VNA | ||||
| from NanoVNASaver.Version import Version | ||||
| 
 | ||||
| logger = logging.getLogger(__name__) | ||||
| 
 | ||||
|  |  | |||
|  | @ -23,7 +23,8 @@ from time import sleep | |||
| from typing import List | ||||
| 
 | ||||
| from NanoVNASaver.Hardware.Serial import Interface | ||||
| from NanoVNASaver.Hardware.VNA import VNA, Version | ||||
| from NanoVNASaver.Hardware.VNA import VNA | ||||
| from NanoVNASaver.Version import Version | ||||
| 
 | ||||
| if platform.system() != 'Windows': | ||||
|     import tty | ||||
|  |  | |||
|  | @ -23,7 +23,7 @@ from typing import List, Iterator | |||
| 
 | ||||
| from PyQt5 import QtGui | ||||
| 
 | ||||
| from NanoVNASaver.Settings import Version | ||||
| from NanoVNASaver.Version import Version | ||||
| from NanoVNASaver.Hardware.Serial import Interface, drain_serial | ||||
| 
 | ||||
| logger = logging.getLogger(__name__) | ||||
|  |  | |||
|  | @ -17,7 +17,6 @@ | |||
| #  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 logging | ||||
| import re | ||||
| import typing | ||||
| from typing import List, Tuple | ||||
| 
 | ||||
|  | @ -152,68 +151,3 @@ class BandsModel(QtCore.QAbstractTableModel): | |||
| 
 | ||||
|     def setColor(self, color): | ||||
|         self.color = color | ||||
| 
 | ||||
| 
 | ||||
| class Version: | ||||
|     RXP = re.compile(r"""^ | ||||
|         \D* | ||||
|         (?P<major>\d+)\. | ||||
|         (?P<minor>\d+)\. | ||||
|         (?P<revision>\d+) | ||||
|         (?P<note>.*) | ||||
|         $""", re.VERBOSE) | ||||
| 
 | ||||
|     def __init__(self, vstring: str = "0.0.0"): | ||||
|         self.data = { | ||||
|             "major": 0, | ||||
|             "minor": 0, | ||||
|             "revision": 0, | ||||
|             "note": "", | ||||
|         } | ||||
|         try: | ||||
|             self.data = Version.RXP.search(vstring).groupdict() | ||||
|             for name in ("major", "minor", "revision"): | ||||
|                 self.data[name] = int(self.data[name]) | ||||
|         except AttributeError: | ||||
|             logger.error("Unable to parse version: %s", vstring) | ||||
| 
 | ||||
|     def __gt__(self, other: "Version") -> bool: | ||||
|         l, r = self.data, other.data | ||||
|         for name in ("major", "minor", "revision"): | ||||
|             if l[name] > r[name]: | ||||
|                 return True | ||||
|             if l[name] < r[name]: | ||||
|                 return False | ||||
|         return False | ||||
| 
 | ||||
|     def __lt__(self, other: "Version") -> bool: | ||||
|         return other > self | ||||
| 
 | ||||
|     def __ge__(self, other: "Version") -> bool: | ||||
|         return self > other or self == other | ||||
| 
 | ||||
|     def __le__(self, other: "Version") -> bool: | ||||
|         return self < other or self == other | ||||
| 
 | ||||
|     def __eq__(self, other: "Version") -> bool: | ||||
|         return self.data == other.data | ||||
| 
 | ||||
|     def __str__(self) -> str: | ||||
|         return (f'{self.data["major"]}.{self.data["minor"]}' | ||||
|                 f'.{self.data["revision"]}{self.data["note"]}') | ||||
| 
 | ||||
|     @property | ||||
|     def major(self) -> int: | ||||
|         return self.data["major"] | ||||
| 
 | ||||
|     @property | ||||
|     def minor(self) -> int: | ||||
|         return self.data["minor"] | ||||
| 
 | ||||
|     @property | ||||
|     def revision(self) -> int: | ||||
|         return self.data["revision"] | ||||
| 
 | ||||
|     @property | ||||
|     def note(self) -> str: | ||||
|         return self.data["note"] | ||||
|  |  | |||
|  | @ -18,43 +18,11 @@ | |||
| #  along with this program.  If not, see <https://www.gnu.org/licenses/>. | ||||
| import logging | ||||
| from math import log | ||||
| from time import sleep | ||||
| from typing import Iterator, List, Tuple | ||||
| 
 | ||||
| import numpy as np | ||||
| from PyQt5 import QtCore, QtWidgets | ||||
| from PyQt5.QtCore import pyqtSlot, pyqtSignal | ||||
| 
 | ||||
| from NanoVNASaver.Calibration import correct_delay | ||||
| from NanoVNASaver.RFTools import Datapoint | ||||
| from typing import Iterator, Tuple | ||||
| 
 | ||||
| logger = logging.getLogger(__name__) | ||||
| 
 | ||||
| 
 | ||||
| def truncate(values: List[List[Tuple]], count: int) -> List[List[Tuple]]: | ||||
|     """truncate drops extrema from data list if averaging is active""" | ||||
|     keep = len(values) - count | ||||
|     logger.debug("Truncating from %d values to %d", len(values), keep) | ||||
|     if count < 1 or keep < 1: | ||||
|         logger.info("Not doing illegal truncate") | ||||
|         return values | ||||
|     truncated = [] | ||||
|     for valueset in np.swapaxes(values, 0, 1).tolist(): | ||||
|         avg = complex(*np.average(valueset, 0)) | ||||
|         truncated.append( | ||||
|             sorted(valueset, | ||||
|                    key=lambda v, a=avg: | ||||
|                    abs(a - complex(*v)))[:keep]) | ||||
|     return np.swapaxes(truncated, 0, 1).tolist() | ||||
| 
 | ||||
| 
 | ||||
| class WorkerSignals(QtCore.QObject): | ||||
|     updated = pyqtSignal() | ||||
|     finished = pyqtSignal() | ||||
|     sweepError = pyqtSignal() | ||||
|     fatalSweepError = pyqtSignal() | ||||
| 
 | ||||
| 
 | ||||
| class Sweep(): | ||||
|     def __init__(self, start: int = 3600000, end: int = 30000000, | ||||
|                  points: int = 101, segments: int = 1, | ||||
|  | @ -116,285 +84,3 @@ class Sweep(): | |||
|             for _ in range(self.points): | ||||
|                 yield round(freq) | ||||
|                 freq += step | ||||
| 
 | ||||
| 
 | ||||
| class SweepWorker(QtCore.QRunnable): | ||||
|     def __init__(self, app: QtWidgets.QWidget): | ||||
|         super().__init__() | ||||
|         logger.info("Initializing SweepWorker") | ||||
|         self.signals = WorkerSignals() | ||||
|         self.app = app | ||||
|         self.sweep = Sweep() | ||||
|         self.setAutoDelete(False) | ||||
|         self.percentage = 0 | ||||
|         self.data11: List[Datapoint] = [] | ||||
|         self.data21: List[Datapoint] = [] | ||||
|         self.rawData11: List[Datapoint] = [] | ||||
|         self.rawData21: List[Datapoint] = [] | ||||
|         self.init_data() | ||||
|         self.stopped = False | ||||
|         self.running = False | ||||
|         self.continuousSweep = False | ||||
|         self.averaging = False | ||||
|         self.averages = 3 | ||||
|         self.truncates = 0 | ||||
|         self.error_message = "" | ||||
|         self.offsetDelay = 0 | ||||
| 
 | ||||
|     @pyqtSlot() | ||||
|     def run(self): | ||||
|         try: | ||||
|             self._run() | ||||
|         except BaseException as exc:  # pylint: disable=broad-except | ||||
|             logger.exception("%s", exc) | ||||
|             self.gui_error(f"ERROR during sweep\n\nStopped\n\n{exc}") | ||||
|             return | ||||
|             # raise exc | ||||
| 
 | ||||
|     def _run(self): | ||||
|         logger.info("Initializing SweepWorker") | ||||
|         self.running = True | ||||
|         self.percentage = 0 | ||||
|         if not self.app.vna.connected(): | ||||
|             logger.debug( | ||||
|                 "Attempted to run without being connected to the NanoVNA") | ||||
|             self.running = False | ||||
|             return | ||||
|         try: | ||||
|             sweep = Sweep( | ||||
|                 self.app.sweep_control.get_start(), | ||||
|                 self.app.sweep_control.get_end(), | ||||
|                 self.app.vna.datapoints, | ||||
|                 self.app.sweep_control.get_segments(), | ||||
|             ) | ||||
|         except ValueError: | ||||
|             self.gui_error( | ||||
|                 "Unable to parse frequency inputs" | ||||
|                 " - check start and stop fields.") | ||||
|             return | ||||
| 
 | ||||
|         averages = 1 | ||||
|         if self.averaging: | ||||
|             logger.info("%d averages", self.averages) | ||||
|             averages = self.averages | ||||
| 
 | ||||
|         if sweep != self.sweep:  # parameters changed | ||||
|             self.sweep = sweep | ||||
|             self.init_data() | ||||
| 
 | ||||
|         finished = False | ||||
|         while not finished: | ||||
|             for i in range(self.sweep.segments): | ||||
|                 logger.debug("Sweep segment no %d", i) | ||||
|                 if self.stopped: | ||||
|                     logger.debug("Stopping sweeping as signalled") | ||||
|                     finished = True | ||||
|                     break | ||||
|                 start, stop = self.sweep.get_index_range(i) | ||||
| 
 | ||||
|                 try: | ||||
|                     freq, values11, values21 = self.readAveragedSegment( | ||||
|                         start, stop, averages) | ||||
|                     self.percentage = (i + 1) * 100 / self.sweep.segments | ||||
|                     self.updateData(freq, values11, values21, i) | ||||
|                 except ValueError as e: | ||||
|                     self.error_message = str(e) | ||||
|                     self.stopped = True | ||||
|                     self.running = False | ||||
|                     self.signals.sweepError.emit() | ||||
| 
 | ||||
|             if not self.continuousSweep: | ||||
|                 finished = True | ||||
| 
 | ||||
|         if self.sweep.segments > 1: | ||||
|             start = self.app.sweep_control.get_start() | ||||
|             end = self.app.sweep_control.get_end() | ||||
|             logger.debug("Resetting NanoVNA sweep to full range: %d to %d", | ||||
|                          start, end) | ||||
|             self.app.vna.resetSweep(start, end) | ||||
| 
 | ||||
|         self.percentage = 100 | ||||
|         logger.debug('Sending "finished" signal') | ||||
|         self.signals.finished.emit() | ||||
|         self.running = False | ||||
| 
 | ||||
|     def init_data(self): | ||||
|         self.data11 = [] | ||||
|         self.data21 = [] | ||||
|         self.rawData11 = [] | ||||
|         self.rawData21 = [] | ||||
|         for freq in self.sweep.get_frequencies(): | ||||
|             self.data11.append(Datapoint(freq, 0.0, 0.0)) | ||||
|             self.data21.append(Datapoint(freq, 0.0, 0.0)) | ||||
|             self.rawData11.append(Datapoint(freq, 0.0, 0.0)) | ||||
|             self.rawData21.append(Datapoint(freq, 0.0, 0.0)) | ||||
|         logger.debug("Init data length: %s", len(self.data11)) | ||||
| 
 | ||||
|     def updateData(self, frequencies, values11, values21, index): | ||||
|         # Update the data from (i*101) to (i+1)*101 | ||||
|         logger.debug( | ||||
|             "Calculating data and inserting in existing data at index %d", | ||||
|             index) | ||||
|         offset = self.sweep.points * index | ||||
|         v11 = values11[:] | ||||
|         v21 = values21[:] | ||||
|         raw_data11 = [] | ||||
|         raw_data21 = [] | ||||
| 
 | ||||
|         for freq in frequencies: | ||||
|             real11, imag11 = v11.pop(0) | ||||
|             real21, imag21 = v21.pop(0) | ||||
|             raw_data11.append(Datapoint(freq, real11, imag11)) | ||||
|             raw_data21.append(Datapoint(freq, real21, imag21)) | ||||
| 
 | ||||
|         data11, data21 = self.applyCalibration(raw_data11, raw_data21) | ||||
|         logger.debug("update Freqs: %s, Offset: %s", len(frequencies), offset) | ||||
|         for i in range(len(frequencies)): | ||||
|             self.data11[offset + i] = data11[i] | ||||
|             self.data21[offset + i] = data21[i] | ||||
|             self.rawData11[offset + i] = raw_data11[i] | ||||
|             self.rawData21[offset + i] = raw_data21[i] | ||||
| 
 | ||||
|         logger.debug("Saving data to application (%d and %d points)", | ||||
|                      len(self.data11), len(self.data21)) | ||||
|         self.app.saveData(self.data11, self.data21) | ||||
|         logger.debug('Sending "updated" signal') | ||||
|         self.signals.updated.emit() | ||||
| 
 | ||||
|     def applyCalibration(self, | ||||
|                          raw_data11: List[Datapoint], | ||||
|                          raw_data21: List[Datapoint] | ||||
|                          ) -> Tuple[List[Datapoint], List[Datapoint]]: | ||||
|         if self.offsetDelay != 0: | ||||
|             tmp = [] | ||||
|             for dp in raw_data11: | ||||
|                 tmp.append(correct_delay(dp, self.offsetDelay, reflect=True)) | ||||
|             raw_data11 = tmp | ||||
|             tmp = [] | ||||
|             for dp in raw_data21: | ||||
|                 tmp.append(correct_delay(dp, self.offsetDelay)) | ||||
|             raw_data21 = tmp | ||||
| 
 | ||||
|         if not self.app.calibration.isCalculated: | ||||
|             return raw_data11, raw_data21 | ||||
| 
 | ||||
|         data11: List[Datapoint] = [] | ||||
|         data21: List[Datapoint] = [] | ||||
| 
 | ||||
|         if self.app.calibration.isValid1Port(): | ||||
|             for dp in raw_data11: | ||||
|                 data11.append(self.app.calibration.correct11(dp)) | ||||
|         else: | ||||
|             data11 = raw_data11 | ||||
| 
 | ||||
|         if self.app.calibration.isValid2Port(): | ||||
|             for dp in raw_data21: | ||||
|                 data21.append(self.app.calibration.correct21(dp)) | ||||
|         else: | ||||
|             data21 = raw_data21 | ||||
|         return data11, data21 | ||||
| 
 | ||||
|     def readAveragedSegment(self, start, stop, averages=1): | ||||
|         values11 = [] | ||||
|         values21 = [] | ||||
|         freq = [] | ||||
|         logger.info("Reading from %d to %d. Averaging %d values", | ||||
|                     start, stop, averages) | ||||
|         for i in range(averages): | ||||
|             if self.stopped: | ||||
|                 logger.debug("Stopping averaging as signalled") | ||||
|                 break | ||||
|             logger.debug("Reading average no %d / %d", i+1, averages) | ||||
|             freq, tmp11, tmp21 = self.readSegment(start, stop) | ||||
|             values11.append(tmp11) | ||||
|             values21.append(tmp21) | ||||
|             self.percentage += 100 / (self.sweep.segments * averages) | ||||
|             self.signals.updated.emit() | ||||
| 
 | ||||
|         if self.truncates and averages > 1: | ||||
|             logger.debug("Truncating %d values by %d", | ||||
|                          len(values11), self.truncates) | ||||
|             values11 = truncate(values11, self.truncates) | ||||
|             values21 = truncate(values21, self.truncates) | ||||
| 
 | ||||
|         logger.debug("Averaging %d values", len(values11)) | ||||
|         values11 = np.average(values11, 0).tolist() | ||||
|         values21 = np.average(values21, 0).tolist() | ||||
| 
 | ||||
|         return freq, values11, values21 | ||||
| 
 | ||||
|     def readSegment(self, start, stop): | ||||
|         logger.debug("Setting sweep range to %d to %d", start, stop) | ||||
|         self.app.vna.setSweep(start, stop) | ||||
| 
 | ||||
|         frequencies = self.app.vna.readFrequencies() | ||||
|         values11 = self.readData("data 0") | ||||
|         values21 = self.readData("data 1") | ||||
|         if (len(frequencies) != len(values11) or | ||||
|                 len(frequencies) != len(values21)): | ||||
|             logger.info("No valid data during this run") | ||||
|             return [], [], [] | ||||
|         return frequencies, values11, values21 | ||||
| 
 | ||||
|     def readData(self, data): | ||||
|         logger.debug("Reading %s", data) | ||||
|         done = False | ||||
|         returndata = [] | ||||
|         count = 0 | ||||
|         while not done: | ||||
|             done = True | ||||
|             returndata = [] | ||||
|             tmpdata = self.app.vna.readValues(data) | ||||
|             logger.debug("Read %d values", len(tmpdata)) | ||||
|             for d in tmpdata: | ||||
|                 a, b = d.split(" ") | ||||
|                 try: | ||||
|                     if self.app.vna.validateInput and ( | ||||
|                             abs(float(a)) > 9.5 or | ||||
|                             abs(float(b)) > 9.5): | ||||
|                         logger.warning( | ||||
|                             "Got a non plausible data value: (%s)", d) | ||||
|                         done = False | ||||
|                         break | ||||
|                     returndata.append((float(a), float(b))) | ||||
|                 except ValueError as exc: | ||||
|                     logger.exception("An exception occurred reading %s: %s", | ||||
|                                      data, exc) | ||||
|                     done = False | ||||
|             if not done: | ||||
|                 logger.debug("Re-reading %s", data) | ||||
|                 sleep(0.2) | ||||
|                 count += 1 | ||||
|                 if count == 5: | ||||
|                     logger.error("Tried and failed to read %s %d times.", | ||||
|                                  data, count) | ||||
|                     logger.debug("trying to reconnect") | ||||
|                     self.app.vna.reconnect() | ||||
|                 if count >= 10: | ||||
|                     logger.critical( | ||||
|                         "Tried and failed to read %s %d times. Giving up.", | ||||
|                         data, count) | ||||
|                     raise IOError( | ||||
|                         f"Failed reading {data} {count} times.\n" | ||||
|                         f"Data outside expected valid ranges," | ||||
|                         f" or in an unexpected format.\n\n" | ||||
|                         f"You can disable data validation on the" | ||||
|                         f"device settings screen.") | ||||
|         return returndata | ||||
| 
 | ||||
|     def setContinuousSweep(self, continuous_sweep: bool): | ||||
|         self.continuousSweep = continuous_sweep | ||||
| 
 | ||||
|     def setAveraging(self, averaging: bool, averages: str, truncates: str): | ||||
|         self.averaging = averaging | ||||
|         try: | ||||
|             self.averages = int(averages) | ||||
|             self.truncates = int(truncates) | ||||
|         except ValueError: | ||||
|             return | ||||
| 
 | ||||
|     def gui_error(self, message: str): | ||||
|         self.error_message = message | ||||
|         self.stopped = True | ||||
|         self.running = False | ||||
|         self.signals.sweepError.emit() | ||||
|  |  | |||
|  | @ -0,0 +1,2 @@ | |||
| from .Bands import BandsModel | ||||
| from .Sweep import Sweep | ||||
|  | @ -17,9 +17,8 @@ | |||
| #  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 logging | ||||
| from math import log | ||||
| from time import sleep | ||||
| from typing import Iterator, List, Tuple | ||||
| from typing import List, Tuple | ||||
| 
 | ||||
| import numpy as np | ||||
| from PyQt5 import QtCore, QtWidgets | ||||
|  | @ -27,6 +26,7 @@ from PyQt5.QtCore import pyqtSlot, pyqtSignal | |||
| 
 | ||||
| from NanoVNASaver.Calibration import correct_delay | ||||
| from NanoVNASaver.RFTools import Datapoint | ||||
| from NanoVNASaver.Settings import Sweep | ||||
| 
 | ||||
| logger = logging.getLogger(__name__) | ||||
| 
 | ||||
|  | @ -55,69 +55,6 @@ class WorkerSignals(QtCore.QObject): | |||
|     fatalSweepError = pyqtSignal() | ||||
| 
 | ||||
| 
 | ||||
| class Sweep(): | ||||
|     def __init__(self, start: int = 3600000, end: int = 30000000, | ||||
|                  points: int = 101, segments: int = 1, | ||||
|                  logarithmic: bool = False): | ||||
|         self.start = start | ||||
|         self.end = end | ||||
|         self.points = points | ||||
|         self.segments = segments | ||||
|         self.logarithmic = logarithmic | ||||
|         self.span = self.end - self.start | ||||
|         self.step = self.stepsize() | ||||
|         self.check() | ||||
| 
 | ||||
|     def __repr__(self) -> str: | ||||
|         return ( | ||||
|             f"Sweep({self.start}, {self.end}, {self.points}, {self.segments}," | ||||
|             f" {self.logarithmic})") | ||||
| 
 | ||||
|     def __eq__(self, other) -> bool: | ||||
|         return(self.start == other.start and | ||||
|                self.end == other.end and | ||||
|                self.points == other.points and | ||||
|                self.segments == other.segments) | ||||
| 
 | ||||
|     def check(self): | ||||
|         if not(self.segments > 0 and | ||||
|                self.points > 0 and | ||||
|                self.start > 0 and | ||||
|                self.end > 0 and | ||||
|                self.stepsize() >= 1): | ||||
|             raise ValueError(f"Illegal sweep settings: {self}") | ||||
| 
 | ||||
|     def stepsize(self) -> int: | ||||
|         return round(self.span / ((self.points -1) * self.segments)) | ||||
| 
 | ||||
|     def _exp_factor(self, index: int) -> int: | ||||
|         return 1 - log(self.segments + 1 - index) / log(self.segments + 1) | ||||
| 
 | ||||
|     def get_index_range(self, index: int) -> Tuple[int, int]: | ||||
|         if not self.logarithmic: | ||||
|             start = self.start + index * self.points * self.step | ||||
|             end = start + (self.points - 1) * self.step | ||||
|         else: | ||||
|             start = self.start + self.span * self._exp_factor(index) | ||||
|             end = self.start + self.span * self._exp_factor(index + 1) | ||||
|         logger.debug("get_index_range(%s) -> (%s, %s)", index, start, end) | ||||
|         return (start, end) | ||||
| 
 | ||||
| 
 | ||||
|     def get_frequencies(self) -> Iterator[int]: | ||||
|         if not self.logarithmic: | ||||
|             for freq in range(self.start, self.end + 1, self.step): | ||||
|                 yield freq | ||||
|             return | ||||
|         for i in range(self.segments): | ||||
|             start, stop = self.get_index_range(i) | ||||
|             step = (stop - start) / self.points | ||||
|             freq = start | ||||
|             for _ in range(self.points): | ||||
|                 yield round(freq) | ||||
|                 freq += step | ||||
| 
 | ||||
| 
 | ||||
| class SweepWorker(QtCore.QRunnable): | ||||
|     def __init__(self, app: QtWidgets.QWidget): | ||||
|         super().__init__() | ||||
|  |  | |||
|  | @ -23,7 +23,7 @@ from urllib import request, error | |||
| from PyQt5 import QtWidgets, QtCore | ||||
| 
 | ||||
| from NanoVNASaver.About import VERSION_URL, INFO_URL | ||||
| from NanoVNASaver.Settings import Version | ||||
| from NanoVNASaver.Version import Version | ||||
| 
 | ||||
| logger = logging.getLogger(__name__) | ||||
| 
 | ||||
|  |  | |||
|  | @ -72,20 +72,24 @@ class SweepSettingsWindow(QtWidgets.QWidget): | |||
| 
 | ||||
|         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.")) | ||||
|         label = QtWidgets.QLabel( | ||||
|             "Averaging allows discarding outlying samples to get better" | ||||
|             "averages. Common values are 3/0, 5/2, 9/4 and 25/6.\n") | ||||
|         label.setWordWrap(True) | ||||
|         settings_layout.addRow(label) | ||||
|   | ||||
|         self.s21att = QtWidgets.QLineEdit("0") | ||||
| 
 | ||||
|         settings_layout.addRow(QtWidgets.QLabel("")) | ||||
|         settings_layout.addRow(QtWidgets.QLabel("Some times when you measure amplifiers you need to use an attenuator")) | ||||
|         settings_layout.addRow(QtWidgets.QLabel("in line with  the S21 input (CH1) here you can specify it.")) | ||||
|         label = QtWidgets.QLabel( | ||||
|             "Some times when you measure amplifiers you need to use an" | ||||
|             " attenuator in line with  the S21 input (CH1) here you can" | ||||
|             " specify it.") | ||||
|         label.setWordWrap(True) | ||||
|         settings_layout.addRow(label) | ||||
| 
 | ||||
|         settings_layout.addRow("Attenuator in port CH1 (s21) in dB", self.s21att) | ||||
|         settings_layout.addRow(QtWidgets.QLabel("Common values with un-un are 16.9 (49:1 2450) 9.54 (9:1 450)")) | ||||
| 
 | ||||
|         # settings_layout.addRow(QtWidgets.QLabel("Common values with un-un are 16.9 (49:1 2450) 9.54 (9:1 450)")) | ||||
| 
 | ||||
|         self.continuous_sweep_radiobutton.toggled.connect( | ||||
|             lambda: self.app.worker.setContinuousSweep( | ||||
|                 self.continuous_sweep_radiobutton.isChecked())) | ||||
|  |  | |||
		Ładowanie…
	
		Reference in New Issue
	
	 Holger Müller
						Holger Müller