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_10.isChecked(): padding = 10 elif self.band_pad_25.isChecked(): padding = 25 elif self.band_pad_100.isChecked(): padding = 100 else: padding = 0 if padding > 0: span = stop - start start -= round(span * padding / 100) start = max(1, start) stop += round(span * padding / 100) self.app.sweepStartInput.setText(RFTools.formatSweepFrequency(start)) self.app.sweepEndInput.setText(RFTools.formatSweepFrequency(stop)) self.app.sweepEndInput.textEdited.emit(self.app.sweepEndInput.text())
def runAnalysis(self): if self.rbtn_data_vswr.isChecked(): suffix = "" data = [] for d in self.app.data: data.append(d.vswr) elif self.rbtn_data_resistance.isChecked(): suffix = " \N{OHM SIGN}" data = [] for d in self.app.data: data.append(d.impedance().real) elif self.rbtn_data_reactance.isChecked(): suffix = " \N{OHM SIGN}" data = [] for d in self.app.data: data.append(d.impedance().imag) elif self.rbtn_data_s21_gain.isChecked(): suffix = " dB" data = [] for d in self.app.data21: data.append(d.gain) else: logger.warning("Searching for peaks on unknown data") return if len(data) == 0: return if self.rbtn_peak_positive.isChecked(): idx_peak = np.argmax(data) elif self.rbtn_peak_negative.isChecked(): idx_peak = np.argmin(data) else: # Both is not yet in logger.warning( "Searching for peaks," " but neither looking at positive nor negative?") return self.peak_frequency.setText( RFTools.formatFrequency(self.app.data[idx_peak].freq)) self.peak_value.setText(str(round(data[idx_peak], 3)) + suffix) if self.checkbox_move_marker.isChecked() and len(self.app.markers) >= 1: self.app.markers[0].setFrequency(str(self.app.data[idx_peak].freq)) self.app.markers[0].frequencyInput.setText( RFTools.formatFrequency(self.app.data[idx_peak].freq))
def calculateRolloff(self, location1, location2): if location1 == location2: return 0, 0 frequency1 = self.app.data21[location1].freq frequency2 = self.app.data21[location2].freq gain1 = RFTools.gain(self.app.data21[location1]) gain2 = RFTools.gain(self.app.data21[location2]) frequency_factor = frequency2 / frequency1 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("%f dB over %f factor", attenuation, frequency_factor) octave_attenuation = attenuation / (math.log10(frequency_factor) / math.log10(2)) decade_attenuation = attenuation / math.log10(frequency_factor) return octave_attenuation, decade_attenuation
def setFrequency(self, frequency): f = RFTools.parseFrequency(frequency) if f > 0: self.frequency = f self.updated.emit() else: self.frequency = 0 self.updated.emit() return
def setMaximumFrequency(self): max_freq_str, selected = QtWidgets.QInputDialog.getText( self, "Stop frequency", "Set stop frequency", text=str(self.maxFrequency)) if not selected: return max_freq = RFTools.parseFrequency(max_freq_str) if max_freq > 0 and not (self.fixedSpan and max_freq <= self.minFrequency): self.maxFrequency = max_freq if self.fixedSpan: self.update()
def run(self): logger.info("Initializing SweepWorker") self.running = True self.percentage = 0 if not self.app.serial.is_open: logger.debug("Attempted to run without being connected to the NanoVNA") self.running = False return if int(self.app.sweepCountInput.text()) > 0: self.noSweeps = int(self.app.sweepCountInput.text()) logger.info("%d sweeps", self.noSweeps) if self.averaging: logger.info("%d averages", self.averages) if self.app.sweepStartInput.text() == "" or self.app.sweepEndInput.text() == "": logger.debug("First sweep - standard range") # We should handle the first startup by reading frequencies? sweep_from = 1000000 sweep_to = 800000000 else: sweep_from = RFTools.parseFrequency(self.app.sweepStartInput.text()) sweep_to = RFTools.parseFrequency(self.app.sweepEndInput.text()) logger.debug("Parsed sweep range as %d to %d", sweep_from, sweep_to) if sweep_from < 0 or sweep_to < 0 or sweep_from == sweep_to: logger.warning("Can't sweep from %s to %s", self.app.sweepStartInput.text(), self.app.sweepEndInput.text()) self.error_message = \ "Unable to parse frequency inputs - check start and stop fields." self.stopped = True self.running = False self.signals.sweepError.emit() return span = sweep_to - sweep_from stepsize = int(span / (self.noSweeps * self.vna.datapoints - 1)) # Setup complete values = [] values21 = [] frequencies = [] if self.averaging: for i in range(self.noSweeps): logger.debug("Sweep segment no %d averaged over %d readings", i, self.averages) if self.stopped: logger.debug("Stopping sweeping as signalled") break start = sweep_from + i * self.vna.datapoints * stepsize freq, val11, val21 = self.readAveragedSegment( start, start + (self.vna.datapoints-1) * stepsize, self.averages) frequencies += freq values += val11 values21 += val21 self.percentage = (i + 1) * (self.vna.datapoints-1) / self.noSweeps logger.debug("Saving acquired data") self.saveData(frequencies, values, values21) else: for i in range(self.noSweeps): logger.debug("Sweep segment no %d", i) if self.stopped: logger.debug("Stopping sweeping as signalled") break start = sweep_from + i*self.vna.datapoints*stepsize try: freq, val11, val21 = self.readSegment( start, start+(self.vna.datapoints-1)*stepsize) frequencies += freq values += val11 values21 += val21 self.percentage = (i+1)*100/self.noSweeps logger.debug("Saving acquired data") self.saveData(frequencies, values, values21) except NanoVNAValueException as e: self.error_message = str(e) self.stopped = True self.running = False self.signals.sweepError.emit() except NanoVNASerialException as e: self.error_message = str(e) self.stopped = True self.running = False self.signals.sweepFatalError.emit() while self.continuousSweep and not self.stopped: logger.debug("Continuous sweeping") for i in range(self.noSweeps): logger.debug("Sweep segment no %d", i) if self.stopped: logger.debug("Stopping sweeping as signalled") break start = sweep_from + i * self.vna.datapoints * stepsize try: _, values, values21 = self.readSegment( start, start + (self.vna.datapoints-1) * stepsize) logger.debug("Updating acquired data") self.updateData(values, values21, i, self.vna.datapoints) except NanoVNAValueException as e: self.error_message = str(e) self.stopped = True self.running = False self.signals.sweepError.emit() except NanoVNASerialException as e: self.error_message = str(e) self.stopped = True self.running = False self.signals.sweepFatalError.emit() # Reset the device to show the full range if we were multisegment if self.noSweeps > 1: logger.debug("Resetting NanoVNA sweep to full range: %d to %d", RFTools.parseFrequency( self.app.sweepStartInput.text()), RFTools.parseFrequency(self.app.sweepEndInput.text())) self.vna.resetSweep(RFTools.parseFrequency(self.app.sweepStartInput.text()), RFTools.parseFrequency(self.app.sweepEndInput.text())) self.percentage = 100 logger.debug("Sending \"finished\" signal") self.signals.finished.emit() self.running = False return
def runAnalysis(self): self.reset() pass_band_location = self.app.markers[0].location logger.debug("Pass band location: %d", pass_band_location) if len(self.app.data21) == 0: logger.debug("No data to analyse") self.result_label.setText("No data to analyse.") return if pass_band_location < 0: logger.debug("No location for %s", self.app.markers[0].name) self.result_label.setText("Please place " + self.app.markers[0].name + " in the passband.") return pass_band_db = RFTools.gain(self.app.data21[pass_band_location]) logger.debug("Initial passband gain: %d", pass_band_db) initial_cutoff_location = -1 for i in range(pass_band_location, len(self.app.data21)): db = RFTools.gain(self.app.data21[i]) if (pass_band_db - db) > 3: # We found a cutoff location initial_cutoff_location = i break if initial_cutoff_location < 0: self.result_label.setText("Cutoff location not found.") return initial_cutoff_frequency = self.app.data21[ initial_cutoff_location].freq logger.debug("Found initial cutoff frequency at %d", initial_cutoff_frequency) peak_location = -1 peak_db = RFTools.gain(self.app.data21[initial_cutoff_location]) for i in range(0, initial_cutoff_location): db = RFTools.gain(self.app.data21[i]) if db > peak_db: peak_db = db peak_location = i logger.debug("Found peak of %f at %d", peak_db, self.app.data[peak_location].freq) self.app.markers[0].setFrequency( str(self.app.data21[peak_location].freq)) self.app.markers[0].frequencyInput.setText( str(self.app.data21[peak_location].freq)) cutoff_location = -1 pass_band_db = peak_db for i in range(peak_location, len(self.app.data21)): db = RFTools.gain(self.app.data21[i]) if (pass_band_db - db) > 3: # We found the cutoff location cutoff_location = i break cutoff_frequency = self.app.data21[cutoff_location].freq cutoff_gain = RFTools.gain( self.app.data21[cutoff_location]) - pass_band_db if cutoff_gain < -4: logger.debug( "Cutoff frequency found at %f dB - insufficient data points for true -3 dB point.", cutoff_gain) logger.debug("Found true cutoff frequency at %d", cutoff_frequency) self.cutoff_label.setText( RFTools.formatFrequency(cutoff_frequency) + " (" + str(round(cutoff_gain, 1)) + " dB)") self.app.markers[1].setFrequency(str(cutoff_frequency)) self.app.markers[1].frequencyInput.setText(str(cutoff_frequency)) six_db_location = -1 for i in range(cutoff_location, len(self.app.data21)): db = RFTools.gain(self.app.data21[i]) if (pass_band_db - db) > 6: # We found 6dB location six_db_location = i break if six_db_location < 0: self.result_label.setText("6 dB location not found.") return six_db_cutoff_frequency = self.app.data21[six_db_location].freq self.six_db_label.setText( RFTools.formatFrequency(six_db_cutoff_frequency)) ten_db_location = -1 for i in range(cutoff_location, len(self.app.data21)): db = RFTools.gain(self.app.data21[i]) if (pass_band_db - db) > 10: # We found 6dB location ten_db_location = i break twenty_db_location = -1 for i in range(cutoff_location, len(self.app.data21)): db = RFTools.gain(self.app.data21[i]) if (pass_band_db - db) > 20: # We found 6dB location twenty_db_location = i break sixty_db_location = -1 for i in range(six_db_location, len(self.app.data21)): db = RFTools.gain(self.app.data21[i]) if (pass_band_db - db) > 60: # We found 60dB location! Wow. sixty_db_location = i break if sixty_db_location > 0: sixty_db_cutoff_frequency = self.app.data21[sixty_db_location].freq self.sixty_db_label.setText( RFTools.formatFrequency(sixty_db_cutoff_frequency)) elif ten_db_location != -1 and twenty_db_location != -1: ten = self.app.data21[ten_db_location].freq twenty = self.app.data21[twenty_db_location].freq sixty_db_frequency = ten * 10**( 5 * (math.log10(twenty) - math.log10(ten))) self.sixty_db_label.setText( RFTools.formatFrequency(sixty_db_frequency) + " (derived)") else: self.sixty_db_label.setText("Not calculated") if ten_db_location > 0 and twenty_db_location > 0 and ten_db_location != twenty_db_location: octave_attenuation, decade_attenuation = self.calculateRolloff( ten_db_location, twenty_db_location) self.db_per_octave_label.setText( str(round(octave_attenuation, 3)) + " dB / octave") self.db_per_decade_label.setText( str(round(decade_attenuation, 3)) + " dB / decade") else: self.db_per_octave_label.setText("Not calculated") self.db_per_decade_label.setText("Not calculated") self.result_label.setText("Analysis complete (" + str(len(self.app.data)) + " points)")
def runAnalysis(self): self.reset() if len(self.app.data21) == 0: logger.debug("No data to analyse") self.result_label.setText("No data to analyse.") return peak_location = -1 peak_db = RFTools.gain(self.app.data21[0]) for i in range(len(self.app.data21)): db = RFTools.gain(self.app.data21[i]) if db > peak_db: peak_db = db peak_location = i logger.debug("Found peak of %f at %d", peak_db, self.app.data[peak_location].freq) lower_cutoff_location = -1 pass_band_db = peak_db for i in range(len(self.app.data21)): db = RFTools.gain(self.app.data21[i]) if (pass_band_db - db) > 3: # We found the cutoff location lower_cutoff_location = i break lower_cutoff_frequency = self.app.data21[lower_cutoff_location].freq lower_cutoff_gain = RFTools.gain( self.app.data21[lower_cutoff_location]) - pass_band_db if lower_cutoff_gain < -4: logger.debug( "Lower cutoff frequency found at %f dB - insufficient data points for true -3 dB point.", lower_cutoff_gain) logger.debug("Found true lower cutoff frequency at %d", lower_cutoff_frequency) self.lower_cutoff_label.setText( RFTools.formatFrequency(lower_cutoff_frequency) + " (" + str(round(lower_cutoff_gain, 1)) + " dB)") self.app.markers[1].setFrequency(str(lower_cutoff_frequency)) self.app.markers[1].frequencyInput.setText(str(lower_cutoff_frequency)) upper_cutoff_location = -1 for i in range(len(self.app.data21) - 1, -1, -1): db = RFTools.gain(self.app.data21[i]) if (pass_band_db - db) > 3: # We found the cutoff location upper_cutoff_location = i break upper_cutoff_frequency = self.app.data21[upper_cutoff_location].freq upper_cutoff_gain = RFTools.gain( self.app.data21[upper_cutoff_location]) - pass_band_db if upper_cutoff_gain < -4: logger.debug( "Upper cutoff frequency found at %f dB - insufficient data points for true -3 dB point.", upper_cutoff_gain) logger.debug("Found true upper cutoff frequency at %d", upper_cutoff_frequency) self.upper_cutoff_label.setText( RFTools.formatFrequency(upper_cutoff_frequency) + " (" + str(round(upper_cutoff_gain, 1)) + " dB)") self.app.markers[2].setFrequency(str(upper_cutoff_frequency)) self.app.markers[2].frequencyInput.setText(str(upper_cutoff_frequency)) span = upper_cutoff_frequency - lower_cutoff_frequency center_frequency = math.sqrt(lower_cutoff_frequency * upper_cutoff_frequency) q = center_frequency / span self.span_label.setText(RFTools.formatFrequency(span)) self.center_frequency_label.setText( RFTools.formatFrequency(center_frequency)) self.quality_label.setText(str(round(q, 2))) self.app.markers[0].setFrequency(str(round(center_frequency))) self.app.markers[0].frequencyInput.setText(str( round(center_frequency))) # Lower roll-off lower_six_db_location = -1 for i in range(lower_cutoff_location, len(self.app.data21)): db = RFTools.gain(self.app.data21[i]) if (pass_band_db - db) > 6: # We found 6dB location lower_six_db_location = i break if lower_six_db_location < 0: self.result_label.setText("Lower 6 dB location not found.") return lower_six_db_cutoff_frequency = self.app.data21[ lower_six_db_location].freq self.lower_six_db_label.setText( RFTools.formatFrequency(lower_six_db_cutoff_frequency)) ten_db_location = -1 for i in range(lower_cutoff_location, len(self.app.data21)): db = RFTools.gain(self.app.data21[i]) if (pass_band_db - db) > 10: # We found 6dB location ten_db_location = i break twenty_db_location = -1 for i in range(lower_cutoff_location, len(self.app.data21)): db = RFTools.gain(self.app.data21[i]) if (pass_band_db - db) > 20: # We found 6dB location twenty_db_location = i break sixty_db_location = -1 for i in range(lower_six_db_location, len(self.app.data21)): db = RFTools.gain(self.app.data21[i]) if (pass_band_db - db) > 60: # We found 60dB location! Wow. sixty_db_location = i break if sixty_db_location > 0: sixty_db_cutoff_frequency = self.app.data21[sixty_db_location].freq self.lower_sixty_db_label.setText( RFTools.formatFrequency(sixty_db_cutoff_frequency)) elif ten_db_location != -1 and twenty_db_location != -1: ten = self.app.data21[ten_db_location].freq twenty = self.app.data21[twenty_db_location].freq sixty_db_frequency = ten * 10**( 5 * (math.log10(twenty) - math.log10(ten))) self.lower_sixty_db_label.setText( RFTools.formatFrequency(sixty_db_frequency) + " (derived)") else: self.lower_sixty_db_label.setText("Not calculated") if ten_db_location > 0 and twenty_db_location > 0 and ten_db_location != twenty_db_location: octave_attenuation, decade_attenuation = self.calculateRolloff( ten_db_location, twenty_db_location) self.lower_db_per_octave_label.setText( str(round(octave_attenuation, 3)) + " dB / octave") self.lower_db_per_decade_label.setText( str(round(decade_attenuation, 3)) + " dB / decade") else: self.lower_db_per_octave_label.setText("Not calculated") self.lower_db_per_decade_label.setText("Not calculated") # Upper roll-off upper_six_db_location = -1 for i in range(upper_cutoff_location, -1, -1): db = RFTools.gain(self.app.data21[i]) if (pass_band_db - db) > 6: # We found 6dB location upper_six_db_location = i break if upper_six_db_location < 0: self.result_label.setText("Upper 6 dB location not found.") return upper_six_db_cutoff_frequency = self.app.data21[ upper_six_db_location].freq self.upper_six_db_label.setText( RFTools.formatFrequency(upper_six_db_cutoff_frequency)) six_db_span = upper_six_db_cutoff_frequency - lower_six_db_cutoff_frequency self.six_db_span_label.setText(RFTools.formatFrequency(six_db_span)) ten_db_location = -1 for i in range(upper_cutoff_location, -1, -1): db = RFTools.gain(self.app.data21[i]) if (pass_band_db - db) > 10: # We found 6dB location ten_db_location = i break twenty_db_location = -1 for i in range(upper_cutoff_location, -1, -1): db = RFTools.gain(self.app.data21[i]) if (pass_band_db - db) > 20: # We found 6dB location twenty_db_location = i break sixty_db_location = -1 for i in range(upper_six_db_location, -1, -1): db = RFTools.gain(self.app.data21[i]) if (pass_band_db - db) > 60: # We found 60dB location! Wow. sixty_db_location = i break if sixty_db_location > 0: sixty_db_cutoff_frequency = self.app.data21[sixty_db_location].freq self.upper_sixty_db_label.setText( RFTools.formatFrequency(sixty_db_cutoff_frequency)) elif ten_db_location != -1 and twenty_db_location != -1: ten = self.app.data21[ten_db_location].freq twenty = self.app.data21[twenty_db_location].freq sixty_db_frequency = ten * 10**( 5 * (math.log10(twenty) - math.log10(ten))) self.upper_sixty_db_label.setText( RFTools.formatFrequency(sixty_db_frequency) + " (derived)") else: self.upper_sixty_db_label.setText("Not calculated") if ten_db_location > 0 and twenty_db_location > 0 and ten_db_location != twenty_db_location: octave_attenuation, decade_attenuation = self.calculateRolloff( ten_db_location, twenty_db_location) self.upper_db_per_octave_label.setText( str(round(octave_attenuation, 3)) + " dB / octave") self.upper_db_per_decade_label.setText( str(round(decade_attenuation, 3)) + " dB / decade") else: self.upper_db_per_octave_label.setText("Not calculated") self.upper_db_per_decade_label.setText("Not calculated") if upper_cutoff_gain < -4 or lower_cutoff_gain < -4: self.result_label.setText( "Analysis complete (" + str(len(self.app.data)) + " points)\n" + "Insufficient data for analysis. Increase segment count.") else: self.result_label.setText("Analysis complete (" + str(len(self.app.data)) + " points)")
def updateLabels(self, s11data: List[Datapoint], s21data: List[Datapoint]): if self.location == -1: return s11 = s11data[self.location] if s21data: s21 = s21data[self.location] imp = s11.impedance() re50, im50 = imp.real, imp.imag vswr = s11.vswr if re50 > 0: rp = (re50 ** 2 + im50 ** 2) / re50 rp = round(rp, 3 - max(0, math.floor(math.log10(abs(rp))))) if rp > 10000: rpstr = str(round(rp/1000, 2)) + "k" elif rp > 1000: rpstr = str(round(rp)) else: rpstr = str(rp) re50 = round(re50, 3 - max(0, math.floor(math.log10(abs(re50))))) if re50 > 10000: re50str = str(round(re50/1000, 2)) + "k" elif re50 > 1000: re50str = str(round(re50)) # Remove the ".0" else: re50str = str(re50) else: rpstr = "-" re50 = 0 re50str = "-" if im50 != 0: xp = (re50 ** 2 + im50 ** 2) / im50 xp = round(xp, 3 - max(0, math.floor(math.log10(abs(xp))))) xpcstr = RFTools.capacitanceEquivalent(xp, s11data[self.location].freq) xplstr = RFTools.inductanceEquivalent(xp, s11data[self.location].freq) if xp < 0: xpstr = xpcstr xp50str = " -j" + str(-1 * xp) else: xpstr = xplstr xp50str = " +j" + str(xp) xp50str += " \N{OHM SIGN}" else: xp50str = " +j ? \N{OHM SIGN}" xpstr = xpcstr = xplstr = "-" if im50 != 0: im50 = round(im50, 3 - max(0, math.floor(math.log10(abs(im50))))) if im50 < 0: im50str = " -j" + str(-1 * im50) else: im50str = " +j" + str(im50) im50str += " \N{OHM SIGN}" self.frequency_label.setText(RFTools.formatFrequency(s11data[self.location].freq)) self.impedance_label.setText(re50str + im50str) self.admittance_label.setText(rpstr + xp50str) self.series_r_label.setText(re50str + " \N{OHM SIGN}") self.parallel_r_label.setText(rpstr + " \N{OHM SIGN}") self.parallel_x_label.setText(xpstr) if self.returnloss_is_positive: returnloss = -round(s11data[self.location].gain, 3) else: returnloss = round(s11data[self.location].gain, 3) self.returnloss_label.setText(str(returnloss) + " dB") capacitance = RFTools.capacitanceEquivalent(im50, s11data[self.location].freq) inductance = RFTools.inductanceEquivalent(im50, s11data[self.location].freq) self.inductance_label.setText(inductance) self.capacitance_label.setText(capacitance) self.parallel_c_label.setText(xpcstr) self.parallel_l_label.setText(xplstr) if im50 > 0: self.series_lc_label.setText(inductance) else: self.series_lc_label.setText(capacitance) vswr = round(vswr, 3) if vswr < 0: vswr = "-" self.vswr_label.setText(str(vswr)) q = s11data[self.location].qFactor() self.quality_factor_label.setText(format_q_factor(q)) self.s11_phase_label.setText( str(round(math.degrees(s11data[self.location].phase), 2)) + "\N{DEGREE SIGN}") fmt = SITools.Format(max_nr_digits=5, space_str=" ") self.s11_group_delay_label.setText(str(SITools.Value(groupDelay(s11data, self.location), "s", fmt))) if len(s21data) == len(s11data): self.gain_label.setText(str(round(s21data[self.location].gain, 3)) + " dB") self.s21_phase_label.setText( str(round(math.degrees(s21data[self.location].phase), 2)) + "\N{DEGREE SIGN}") self.s21_group_delay_label.setText(str(SITools.Value(groupDelay(s21data, self.location) / 2, "s", fmt)))
def setFrequency(self, frequency): f = RFTools.parseFrequency(frequency) self.frequency = max(f, 0) self.updated.emit(self)
# 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 unittest # Import targets to be tested from NanoVNASaver.RFTools import RFTools rft = RFTools() # TODO: should be tested against SITools.Value # RFTools.parseFrequency will hopefully go away in future # and be specialised by input field and device, like # parse_clamp_min=50000 for sweep input with # a nanovna version 1 attached ... # the hardware developer already announced a successor # which will have different limits class TestCases(unittest.TestCase): def test_basicSIUnits(self): # simple well-formed integers with correct SI units self.assertEqual(rft.parseFrequency('123Hz'), 123) self.assertEqual(rft.parseFrequency('123456Hz'), 123456) self.assertEqual(rft.parseFrequency('123kHz'), 123000)
def updateLabels(self, s11data: List[Datapoint], s21data: List[Datapoint]): if self.location != -1: re50, im50 = RFTools.normalize50(s11data[self.location]) vswr = RFTools.calculateVSWR(s11data[self.location]) if re50 > 0: rp = (re50**2 + im50**2) / re50 rp = round(rp, 3 - max(0, math.floor(math.log10(abs(rp))))) if rp > 10000: rpstr = str(round(rp / 1000, 2)) + "k" elif rp > 1000: rpstr = str(round(rp)) else: rpstr = str(rp) re50 = round(re50, 3 - max(0, math.floor(math.log10(abs(re50))))) if re50 > 10000: re50str = str(round(re50 / 1000, 2)) + "k" elif re50 > 1000: re50str = str(round(re50)) # Remove the ".0" else: re50str = str(re50) else: rpstr = "-" re50 = 0 re50str = "-" if im50 != 0: xp = (re50**2 + im50**2) / im50 xp = round(xp, 3 - max(0, math.floor(math.log10(abs(xp))))) xpcstr = RFTools.capacitanceEquivalent( xp, s11data[self.location].freq) xplstr = RFTools.inductanceEquivalent( xp, s11data[self.location].freq) if xp < 0: xpstr = xpcstr xp50str = " -j" + str(-1 * xp) else: xpstr = xplstr xp50str = " +j" + str(xp) xp50str += " \N{OHM SIGN}" else: xp50str = " +j ? \N{OHM SIGN}" xpstr = xpcstr = xplstr = "-" if im50 != 0: im50 = round(im50, 3 - max(0, math.floor(math.log10(abs(im50))))) if im50 < 0: im50str = " -j" + str(-1 * im50) else: im50str = " +j" + str(im50) im50str += " \N{OHM SIGN}" self.frequency_label.setText( RFTools.formatFrequency(s11data[self.location].freq)) self.impedance_label.setText(re50str + im50str) self.admittance_label.setText(rpstr + xp50str) self.series_r_label.setText(re50str + " \N{OHM SIGN}") self.parallel_r_label.setText(rpstr + " \N{OHM SIGN}") self.parallel_x_label.setText(xpstr) if self.returnloss_is_positive: returnloss = -round(RFTools.gain(s11data[self.location]), 3) else: returnloss = round(RFTools.gain(s11data[self.location]), 3) self.returnloss_label.setText(str(returnloss) + " dB") capacitance = RFTools.capacitanceEquivalent( im50, s11data[self.location].freq) inductance = RFTools.inductanceEquivalent( im50, s11data[self.location].freq) self.inductance_label.setText(inductance) self.capacitance_label.setText(capacitance) self.parallel_c_label.setText(xpcstr) self.parallel_l_label.setText(xplstr) if im50 > 0: self.series_lc_label.setText(inductance) else: self.series_lc_label.setText(capacitance) vswr = round(vswr, 3) if vswr < 0: vswr = "-" self.vswr_label.setText(str(vswr)) q = RFTools.qualityFactor(s11data[self.location]) if q > 10000 or q < 0: q_str = "\N{INFINITY}" elif q > 1000: q_str = str(round(q, 0)) elif q > 100: q_str = str(round(q, 1)) elif q > 10: q_str = str(round(q, 2)) else: q_str = str(round(q, 3)) self.quality_factor_label.setText(q_str) self.s11_phase_label.setText( str(round(RFTools.phaseAngle(s11data[self.location]), 2)) + "\N{DEGREE SIGN}") if len(s21data) == len(s11data): self.gain_label.setText( str(round(RFTools.gain(s21data[self.location]), 3)) + " dB") self.s21_phase_label.setText( str(round(RFTools.phaseAngle(s21data[self.location]), 2)) + "\N{DEGREE SIGN}")
def runAnalysis(self): max_dips_shown = 3 data = [] for d in self.app.data: data.append(d.vswr) # min_idx = np.argmin(data) # # logger.debug("Minimum at %d", min_idx) # logger.debug("Value at minimum: %f", data[min_idx]) # logger.debug("Frequency: %d", self.app.data[min_idx].freq) # # if self.checkbox_move_marker.isChecked(): # self.app.markers[0].setFrequency(str(self.app.data[min_idx].freq)) # self.app.markers[0].frequencyInput.setText(str(self.app.data[min_idx].freq)) minimums = [] min_start = -1 min_idx = -1 threshold = self.input_vswr_limit.value() 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 logger.debug("Found %d sections under %f threshold", len(minimums), threshold) results_header = self.layout.indexOf(self.results_label) logger.debug("Results start at %d, out of %d", results_header, self.layout.rowCount()) for i in range(results_header, self.layout.rowCount()): self.layout.removeRow(self.layout.rowCount() - 1) if len(minimums) > max_dips_shown: self.layout.addRow( QtWidgets.QLabel("<b>More than " + str(max_dips_shown) + " dips found. Lowest shown.</b>")) dips = [] for m in minimums: start, lowest, end = m dips.append(data[lowest]) best_dips = [] for i in range(max_dips_shown): min_idx = np.argmin(dips) best_dips.append(minimums[min_idx]) dips.remove(dips[min_idx]) minimums.remove(minimums[min_idx]) minimums = best_dips if len(minimums) > 0: for m in minimums: start, lowest, end = m if start != end: logger.debug("Section from %d to %d, lowest at %d", start, end, lowest) self.layout.addRow( "Start", QtWidgets.QLabel( RFTools.formatFrequency( self.app.data[start].freq))) self.layout.addRow( "Minimum", QtWidgets.QLabel( RFTools.formatFrequency(self.app.data[lowest].freq) + " (" + str(round(data[lowest], 2)) + ")")) self.layout.addRow( "End", QtWidgets.QLabel( RFTools.formatFrequency(self.app.data[end].freq))) self.layout.addRow( "Span", QtWidgets.QLabel( RFTools.formatFrequency( self.app.data[end].freq - self.app.data[start].freq))) self.layout.addWidget(PeakSearchAnalysis.QHLine()) else: self.layout.addRow( "Low spot", QtWidgets.QLabel( RFTools.formatFrequency( self.app.data[lowest].freq))) self.layout.addWidget(PeakSearchAnalysis.QHLine()) # Remove the final separator line self.layout.removeRow(self.layout.rowCount() - 1) else: self.layout.addRow( QtWidgets.QLabel("No areas found with VSWR below " + str(round(threshold, 2)) + "."))