def __init__(self, modulators, tree_model=None, parent=None): """ :type modulators: list of Modulator """ super().__init__(parent) self.ui = Ui_DialogModulation() self.ui.setupUi(self) self.setAttribute(Qt.WA_DeleteOnClose) self.setWindowFlags(Qt.Window) self.lock_samples_in_view = False if tree_model is not None: self.ui.treeViewSignals.setModel(tree_model) self.ui.treeViewSignals.expandAll() self.ui.gVOriginalSignal.signal_tree_root = tree_model.rootItem self.ui.comboBoxCustomModulations.clear() for modulator in modulators: self.ui.comboBoxCustomModulations.addItem(modulator.name) if len(modulators) == 1: self.ui.btnRemoveModulation.setDisabled(True) self.modulators = modulators for graphic_view in (self.ui.gVCarrier, self.ui.gVData): graphic_view.scene_y_min = -1 graphic_view.scene_y_max = 1 graphic_view.scene_x_zoom_stretch = 1.1 min_max_mod = IQArray.min_max_for_dtype(self.current_modulator.get_dtype()) self.ui.gVModulated.scene_y_min = min_max_mod[0] self.ui.gVModulated.scene_y_max = min_max_mod[1] self.ui.gVModulated.scene_x_zoom_stretch = 1.1 self.set_ui_for_current_modulator() self.ui.cbShowDataBitsOnly.setText(self.tr("Show Only Data Sequence\n")) self.ui.cbShowDataBitsOnly.setEnabled(False) self.protocol = None # type: ProtocolAnalyzer self.search_results = [] self.ui.cbShowDataBitsOnly.setEnabled(False) self.ui.btnSearchNext.setEnabled(False) self.ui.btnSearchPrev.setEnabled(False) self.ui.chkBoxLockSIV.setDisabled(True) self.original_bits = "" self.ui.btnRestoreBits.setEnabled(False) self.create_connects() try: self.restoreGeometry(constants.SETTINGS.value("{}/geometry".format(self.__class__.__name__))) except TypeError: pass self.set_modulation_profile_status()
def __init__(self, modulators, tree_model=None, parent=None): """ :type modulators: list of Modulator """ super().__init__(parent) self.ui = Ui_DialogModulation() self.ui.setupUi(self) self.setAttribute(Qt.WA_DeleteOnClose) self.setWindowFlags(Qt.Window) self.lock_samples_in_view = False if tree_model is not None: self.ui.treeViewSignals.setModel(tree_model) self.ui.treeViewSignals.expandAll() self.ui.gVOriginalSignal.signal_tree_root = tree_model.rootItem self.ui.comboBoxCustomModulations.clear() for modulator in modulators: self.ui.comboBoxCustomModulations.addItem(modulator.name) if len(modulators) == 1: self.ui.btnRemoveModulation.setDisabled(True) self.modulators = modulators self.set_ui_for_current_modulator() self.ui.cbShowDataBitsOnly.setText( self.tr("Show Only Data Sequence\n")) self.ui.cbShowDataBitsOnly.setEnabled(False) self.protocol = None # type: ProtocolAnalyzer self.search_results = [] self.ui.cbShowDataBitsOnly.setEnabled(False) self.ui.btnSearchNext.setEnabled(False) self.ui.btnSearchPrev.setEnabled(False) self.ui.chkBoxLockSIV.setDisabled(True) self.original_bits = "" self.restore_bits_action = self.ui.linEdDataBits.addAction( QIcon.fromTheme("edit-undo"), QLineEdit.TrailingPosition) self.restore_bits_action.setEnabled(False) self.configure_parameters_action = self.ui.lineEditParameters.addAction( QIcon.fromTheme("configure"), QLineEdit.TrailingPosition) self.create_connects() self.restoreGeometry( settings.read("{}/geometry".format(self.__class__.__name__), type=bytes)) self.set_bits_per_symbol_enabled_status() self.set_modulation_profile_status() # Ensure full srceen shown after resize QTimer.singleShot(100, self.show_full_scene)
def __init__(self, modulators, parent=None): """ :type modulators: list of Modulator """ super().__init__(parent) self.ui = Ui_DialogModulation() self.ui.setupUi(self) self.setAttribute(Qt.WA_DeleteOnClose) self.lock_samples_in_view = False self.ui.comboBoxCustomModulations.clear() for modulator in modulators: self.ui.comboBoxCustomModulations.addItem(modulator.name) if len(modulators) == 1: self.ui.btnRemoveModulation.setDisabled(True) self.modulators = modulators for graphic_view in (self.ui.gVCarrier, self.ui.gVData, self.ui.gVModulated): graphic_view.scene_y_min = -1 graphic_view.scene_y_max = 1 graphic_view.scene_x_zoom_stretch = 1.1 self.set_ui_for_current_modulator() self.ui.cbShowDataBitsOnly.setText( self.tr("Show Only Data Sequence\n")) self.ui.cbShowDataBitsOnly.setEnabled(False) self.protocol = None # type: ProtocolAnalyzer self.search_results = [] self.ui.cbShowDataBitsOnly.setEnabled(False) self.ui.btnSearchNext.setEnabled(False) self.ui.btnSearchPrev.setEnabled(False) self.ui.chkBoxLockSIV.setDisabled(True) self.original_bits = "" self.ui.btnRestoreBits.setEnabled(False) self.create_connects() try: self.restoreGeometry( constants.SETTINGS.value("{}/geometry".format( self.__class__.__name__))) except TypeError: pass
def __init__(self, modulators, parent=None): """ :type modulators: list of Modulator """ super().__init__(parent) self.ui = Ui_DialogModulation() self.ui.setupUi(self) self.setAttribute(Qt.WA_DeleteOnClose) self.lock_samples_in_view = False self.ui.comboBoxCustomModulations.clear() for modulator in modulators: self.ui.comboBoxCustomModulations.addItem(modulator.name) if len(modulators) == 1: self.ui.btnRemoveModulation.setDisabled(True) self.modulators = modulators self.ui.doubleSpinBoxCarrierFreq.setValue( self.current_modulator.carrier_freq_hz) self.ui.doubleSpinBoxCarrierPhase.setValue( self.current_modulator.carrier_phase_deg) self.ui.spinBoxBitLength.setValue( self.current_modulator.samples_per_bit) self.ui.spinBoxSampleRate.setValue(self.current_modulator.sample_rate) self.ui.spinBoxParameter0.setValue( self.current_modulator.param_for_zero) self.ui.spinBoxParameter1.setValue( self.current_modulator.param_for_one) self.ui.cbShowDataBitsOnly.setText( self.tr("Show Only Data Sequence\n")) self.ui.cbShowDataBitsOnly.setEnabled(False) self.protocol = None """:type: ProtocolAnalyzer """ self.search_results = [] self.ui.cbShowDataBitsOnly.setEnabled(False) self.ui.btnSearchNext.setEnabled(False) self.ui.btnSearchPrev.setEnabled(False) self.ui.chkBoxLockSIV.setDisabled(True) self.ui.comboBoxModulationType.setCurrentIndex( self.current_modulator.modulation_type) self.create_connects() self.on_modulation_type_changed() self.original_bits = "" self.ui.btnRestoreBits.setEnabled(False)
def __init__(self, modulators, tree_model=None, parent=None): """ :type modulators: list of Modulator """ super().__init__(parent) self.ui = Ui_DialogModulation() self.ui.setupUi(self) self.setAttribute(Qt.WA_DeleteOnClose) self.setWindowFlags(Qt.Window) self.lock_samples_in_view = False if tree_model is not None: self.ui.treeViewSignals.setModel(tree_model) self.ui.treeViewSignals.expandAll() self.ui.gVOriginalSignal.signal_tree_root = tree_model.rootItem self.ui.comboBoxCustomModulations.clear() for modulator in modulators: self.ui.comboBoxCustomModulations.addItem(modulator.name) if len(modulators) == 1: self.ui.btnRemoveModulation.setDisabled(True) self.modulators = modulators for graphic_view in (self.ui.gVCarrier, self.ui.gVData, self.ui.gVModulated): graphic_view.scene_y_min = -1 graphic_view.scene_y_max = 1 graphic_view.scene_x_zoom_stretch = 1.1 self.set_ui_for_current_modulator() self.ui.cbShowDataBitsOnly.setText(self.tr("Show Only Data Sequence\n")) self.ui.cbShowDataBitsOnly.setEnabled(False) self.protocol = None # type: ProtocolAnalyzer self.search_results = [] self.ui.cbShowDataBitsOnly.setEnabled(False) self.ui.btnSearchNext.setEnabled(False) self.ui.btnSearchPrev.setEnabled(False) self.ui.chkBoxLockSIV.setDisabled(True) self.original_bits = "" self.ui.btnRestoreBits.setEnabled(False) self.create_connects() try: self.restoreGeometry(constants.SETTINGS.value("{}/geometry".format(self.__class__.__name__))) except TypeError: pass self.set_modulation_profile_status()
def __init__(self, modulators, parent=None): """ :type modulators: list of Modulator """ super().__init__(parent) self.ui = Ui_DialogModulation() self.ui.setupUi(self) self.setAttribute(Qt.WA_DeleteOnClose) self.lock_samples_in_view = False self.ui.comboBoxCustomModulations.clear() for modulator in modulators: self.ui.comboBoxCustomModulations.addItem(modulator.name) if len(modulators) == 1: self.ui.btnRemoveModulation.setDisabled(True) self.modulators = modulators self.set_ui_for_current_modulator() self.ui.cbShowDataBitsOnly.setText( self.tr("Show Only Data Sequence\n")) self.ui.cbShowDataBitsOnly.setEnabled(False) self.protocol = None # type: ProtocolAnalyzer self.search_results = [] self.ui.cbShowDataBitsOnly.setEnabled(False) self.ui.btnSearchNext.setEnabled(False) self.ui.btnSearchPrev.setEnabled(False) self.ui.chkBoxLockSIV.setDisabled(True) self.create_connects() self.on_modulation_type_changed() self.original_bits = "" self.ui.btnRestoreBits.setEnabled(False)
class ModulatorDialog(QDialog): def __init__(self, modulators, parent=None): """ :type modulators: list of Modulator """ super().__init__(parent) self.ui = Ui_DialogModulation() self.ui.setupUi(self) self.setAttribute(Qt.WA_DeleteOnClose) self.lock_samples_in_view = False self.ui.comboBoxCustomModulations.clear() for modulator in modulators: self.ui.comboBoxCustomModulations.addItem(modulator.name) if len(modulators) == 1: self.ui.btnRemoveModulation.setDisabled(True) self.modulators = modulators for graphic_view in (self.ui.gVCarrier, self.ui.gVData, self.ui.gVModulated): graphic_view.scene_y_min = -1 graphic_view.scene_y_max = 1 graphic_view.scene_x_zoom_stretch = 1.1 self.set_ui_for_current_modulator() self.ui.cbShowDataBitsOnly.setText( self.tr("Show Only Data Sequence\n")) self.ui.cbShowDataBitsOnly.setEnabled(False) self.protocol = None # type: ProtocolAnalyzer self.search_results = [] self.ui.cbShowDataBitsOnly.setEnabled(False) self.ui.btnSearchNext.setEnabled(False) self.ui.btnSearchPrev.setEnabled(False) self.ui.chkBoxLockSIV.setDisabled(True) self.original_bits = "" self.ui.btnRestoreBits.setEnabled(False) self.create_connects() try: self.restoreGeometry( constants.SETTINGS.value("{}/geometry".format( self.__class__.__name__))) except TypeError: pass self.set_modulation_profile_status() def __cur_selected_mod_type(self): s = self.ui.comboBoxModulationType.currentText() return s[s.rindex("(") + 1:s.rindex(")")] @staticmethod def __trim_number(number): if abs(number) >= 1e9: # giga return numpy.round(number / 1e9) * 1e9 elif abs(number) >= 1e6: # mega return numpy.round(number / 1e6) * 1e6 elif abs(number) >= 1e3: # Kilo return numpy.round(number / 1e3) * 1e3 else: return number @staticmethod def __ensure_multitude(num1, num2): try: if abs(num1) > abs(num2): num1 = abs(int(num1 / num2)) * num2 else: num2 = abs(int(num2 / num1)) * num1 return num1, num2 except Exception: return num1, num2 def __set_gauss_ui_visibility(self, show: bool): self.ui.lGaussBT.setVisible(show) self.ui.lGaussWidth.setVisible(show) self.ui.spinBoxGaussBT.setVisible(show) self.ui.spinBoxGaussFilterWidth.setVisible(show) self.ui.spinBoxGaussFilterWidth.setValue( self.current_modulator.gauss_filter_width) self.ui.spinBoxGaussBT.setValue(self.current_modulator.gauss_bt) def closeEvent(self, event: QCloseEvent): constants.SETTINGS.setValue( "{}/geometry".format(self.__class__.__name__), self.saveGeometry()) for gv in (self.ui.gVCarrier, self.ui.gVData, self.ui.gVModulated, self.ui.gVOriginalSignal): # Eliminate graphic views to prevent segfaults gv.eliminate() super().closeEvent(event) @property def current_modulator(self): mod = self.modulators[self.ui.comboBoxCustomModulations.currentIndex()] return mod def set_ui_for_current_modulator(self): index = self.ui.comboBoxModulationType.findText( "*(" + self.current_modulator.modulation_type_str + ")", Qt.MatchWildcard) self.ui.comboBoxModulationType.setCurrentIndex(index) self.ui.doubleSpinBoxCarrierFreq.setValue( self.current_modulator.carrier_freq_hz) self.ui.doubleSpinBoxCarrierPhase.setValue( self.current_modulator.carrier_phase_deg) self.ui.spinBoxBitLength.setValue( self.current_modulator.samples_per_bit) self.ui.spinBoxSampleRate.setValue(self.current_modulator.sample_rate) self.ui.spinBoxParameter0.setValue( self.current_modulator.param_for_zero) self.ui.spinBoxParameter1.setValue( self.current_modulator.param_for_one) def create_connects(self): self.ui.doubleSpinBoxCarrierFreq.valueChanged.connect( self.on_carrier_freq_changed) self.ui.doubleSpinBoxCarrierPhase.valueChanged.connect( self.on_carrier_phase_changed) self.ui.spinBoxBitLength.valueChanged.connect(self.on_bit_len_changed) self.ui.spinBoxSampleRate.valueChanged.connect( self.on_sample_rate_changed) self.ui.linEdDataBits.textChanged.connect(self.on_data_bits_changed) self.ui.spinBoxParameter0.valueChanged.connect( self.on_modulation_parameter_zero_changed) self.ui.spinBoxParameter1.valueChanged.connect( self.on_modulation_parameter_one_changed) self.ui.comboBoxModulationType.currentIndexChanged.connect( self.on_modulation_type_changed) self.ui.gVOriginalSignal.zoomed.connect(self.on_orig_signal_zoomed) self.ui.cbShowDataBitsOnly.stateChanged.connect( self.on_show_data_bits_only_changed) self.ui.btnSearchNext.clicked.connect( self.on_btn_next_search_result_clicked) self.ui.btnSearchPrev.clicked.connect( self.on_btn_prev_search_result_clicked) self.ui.comboBoxCustomModulations.editTextChanged.connect( self.on_custom_modulation_name_edited) self.ui.comboBoxCustomModulations.currentIndexChanged.connect( self.on_custom_modulation_index_changed) self.ui.btnAddModulation.clicked.connect(self.add_modulator) self.ui.btnRemoveModulation.clicked.connect( self.on_remove_modulator_clicked) self.ui.gVModulated.zoomed.connect( self.on_carrier_data_modulated_zoomed) self.ui.gVCarrier.zoomed.connect(self.on_carrier_data_modulated_zoomed) self.ui.gVData.zoomed.connect(self.on_carrier_data_modulated_zoomed) self.ui.gVModulated.selection_width_changed.connect( self.on_modulated_selection_changed) self.ui.gVOriginalSignal.selection_width_changed.connect( self.on_original_selection_changed) self.ui.spinBoxGaussBT.valueChanged.connect(self.on_gauss_bt_changed) self.ui.spinBoxGaussFilterWidth.valueChanged.connect( self.on_gaus_filter_wdith_changed) self.ui.chkBoxLockSIV.stateChanged.connect(self.on_lock_siv_changed) self.ui.btnRestoreBits.clicked.connect( self.on_btn_restore_bits_clicked) self.ui.btnSaveAndClose.clicked.connect(self.close) self.ui.gVOriginalSignal.signal_loaded.connect( self.handle_signal_loaded) self.ui.btnAutoDetect.clicked.connect(self.on_btn_autodetect_clicked) def draw_carrier(self): self.ui.gVCarrier.plot_data(self.current_modulator.carrier_data) def draw_data_bits(self): self.ui.gVData.setScene(self.current_modulator.data_scene) self.ui.gVData.update() def draw_modulated(self): self.current_modulator.modulate(pause=0) self.ui.gVModulated.plot_data( self.current_modulator.modulated_samples.imag.astype( numpy.float32)) if self.lock_samples_in_view: siv = self.ui.gVOriginalSignal.view_rect().width() self.adjust_samples_in_view(siv) else: self.mark_samples_in_view() def draw_original_signal(self, start=0, end=-1): scene_manager = self.ui.gVOriginalSignal.scene_manager if scene_manager is None: return if end == -1: end = scene_manager.signal.num_samples y = scene_manager.scene.sceneRect().y() h = scene_manager.scene.sceneRect().height() self.ui.gVOriginalSignal.setSceneRect(start, y, end - start, h) self.ui.gVOriginalSignal.fitInView( self.ui.gVOriginalSignal.sceneRect()) scene_manager.show_scene_section(start, end) self.ui.gVOriginalSignal.update() if self.lock_samples_in_view: self.adjust_samples_in_view( self.ui.gVModulated.view_rect().width()) else: self.mark_samples_in_view() def update_views(self): self.ui.gVCarrier.update() self.ui.gVData.update() self.ui.gVModulated.update() self.ui.gVOriginalSignal.update() def search_data_sequence(self): if not self.ui.cbShowDataBitsOnly.isEnabled( ) or not self.ui.cbShowDataBitsOnly.isChecked(): return search_seq = self.ui.linEdDataBits.text() if len(search_seq) == 0 or self.protocol is None: return self.search_results[:] = [] proto_bits = self.protocol.plain_bits_str len_seq = len(search_seq) for i, message in enumerate(proto_bits): j = message.find(search_seq) while j != -1: self.search_results.append((i, j, j + len_seq)) j = message.find(search_seq, j + 1) self.ui.lTotalSearchresults.setText(str(len(self.search_results))) self.show_search_result(0) def show_search_result(self, i: int): if len(self.search_results) == 0: self.ui.lCurrentSearchResult.setText("0") self.ui.gVOriginalSignal.scene_manager.clear_path() return message, start_index, end_index = self.search_results[i] start, nsamples = self.protocol.get_samplepos_of_bitseq( message, start_index, message, end_index, False) self.draw_original_signal(start=start, end=start + nsamples) self.ui.lCurrentSearchResult.setText(str(i + 1)) if i == len(self.search_results) - 1: self.ui.btnSearchNext.setEnabled(False) else: self.ui.btnSearchNext.setEnabled(True) if i == 0: self.ui.btnSearchPrev.setEnabled(False) else: self.ui.btnSearchPrev.setEnabled(True) def add_modulator(self): names = [m.name for m in self.modulators] name = "Modulation" number = 1 while name in names: name = "Modulation " + str(number) number += 1 self.modulators.append(Modulator(name)) self.ui.comboBoxCustomModulations.addItem(name) self.ui.comboBoxCustomModulations.setCurrentIndex( len(self.modulators) - 1) self.ui.btnRemoveModulation.setEnabled(True) def adjust_samples_in_view(self, target_siv: float): self.ui.gVOriginalSignal.scale( self.ui.gVOriginalSignal.view_rect().width() / target_siv, 1) mod_zoom_factor = self.ui.gVModulated.view_rect().width() / target_siv self.ui.gVModulated.scale(mod_zoom_factor, 1) self.ui.gVCarrier.scale(mod_zoom_factor, 1) self.ui.gVData.scale(mod_zoom_factor, 1) self.mark_samples_in_view() def autodetect_fsk_freqs(self): if self.__cur_selected_mod_type() not in ("FSK", "GFSK"): return try: zero_freq = self.protocol.estimate_frequency_for_zero( self.current_modulator.sample_rate) one_freq = self.protocol.estimate_frequency_for_one( self.current_modulator.sample_rate) zero_freq = self.__trim_number(zero_freq) one_freq = self.__trim_number(one_freq) zero_freq, one_freq = self.__ensure_multitude(zero_freq, one_freq) if zero_freq == one_freq: # If frequencies are equal, it is very probable the zero freq is negative zero_freq = -one_freq self.ui.spinBoxParameter0.setValue(zero_freq) self.ui.spinBoxParameter1.setValue(one_freq) except AttributeError: self.ui.spinBoxParameter0.setValue( self.current_modulator.carrier_freq_hz / 2) self.ui.spinBoxParameter1.setValue( self.current_modulator.carrier_freq_hz) def handle_signal_loaded(self, protocol): self.setCursor(Qt.WaitCursor) self.ui.cbShowDataBitsOnly.setEnabled(True) self.ui.chkBoxLockSIV.setEnabled(True) self.ui.btnAutoDetect.setEnabled(True) self.protocol = protocol # Apply bit length of original signal to current modulator self.ui.spinBoxBitLength.setValue( self.ui.gVOriginalSignal.signal.bit_len) # https://github.com/jopohl/urh/issues/130 self.ui.gVModulated.show_full_scene(reinitialize=True) self.ui.gVCarrier.show_full_scene(reinitialize=True) self.ui.gVData.show_full_scene(reinitialize=True) self.unsetCursor() def mark_samples_in_view(self): self.ui.lSamplesInViewModulated.setText( str(int(self.ui.gVModulated.view_rect().width()))) if self.ui.gVOriginalSignal.scene_manager is not None: self.ui.lSamplesInViewOrigSignal.setText( str(int(self.ui.gVOriginalSignal.view_rect().width()))) else: self.ui.lSamplesInViewOrigSignal.setText("-") return if int(self.ui.gVOriginalSignal.view_rect().width()) != int( self.ui.gVModulated.view_rect().width()): font = self.ui.lSamplesInViewModulated.font() font.setBold(False) self.ui.lSamplesInViewModulated.setFont(font) self.ui.lSamplesInViewOrigSignal.setFont(font) self.ui.lSamplesInViewOrigSignal.setStyleSheet( "QLabel { color : red; }") self.ui.lSamplesInViewModulated.setStyleSheet( "QLabel { color : red; }") else: font = self.ui.lSamplesInViewModulated.font() font.setBold(True) self.ui.lSamplesInViewModulated.setFont(font) self.ui.lSamplesInViewOrigSignal.setFont(font) self.ui.lSamplesInViewOrigSignal.setStyleSheet("") self.ui.lSamplesInViewModulated.setStyleSheet("") def set_modulation_profile_status(self): visible = constants.SETTINGS.value("multiple_modulations", False, bool) self.ui.btnAddModulation.setVisible(visible) self.ui.btnRemoveModulation.setVisible(visible) self.ui.comboBoxCustomModulations.setVisible(visible) def resizeEvent(self, event: QResizeEvent): self.update_views() def keyPressEvent(self, event: QKeyEvent): if event.key() == Qt.Key_Enter or event.key() == Qt.Key_Return: return else: super().keyPressEvent(event) @pyqtSlot() def on_carrier_freq_changed(self): self.current_modulator.carrier_freq_hz = self.ui.doubleSpinBoxCarrierFreq.value( ) self.draw_carrier() self.draw_modulated() @pyqtSlot() def on_carrier_phase_changed(self): self.current_modulator.carrier_phase_deg = self.ui.doubleSpinBoxCarrierPhase.value( ) self.draw_carrier() self.draw_modulated() @pyqtSlot() def on_bit_len_changed(self): self.current_modulator.samples_per_bit = self.ui.spinBoxBitLength.value( ) self.draw_carrier() self.draw_data_bits() self.draw_modulated() for graphic_view in (self.ui.gVModulated, self.ui.gVData, self.ui.gVCarrier): graphic_view.show_full_scene(reinitialize=True) @pyqtSlot() def on_data_bits_changed(self): text = self.ui.linEdDataBits.text() text = ''.join(c for c in text if c == "1" or c == "0") self.ui.linEdDataBits.blockSignals(True) self.ui.linEdDataBits.setText(text) self.ui.linEdDataBits.blockSignals(False) self.current_modulator.display_bits = text self.draw_carrier() self.draw_data_bits() self.draw_modulated() if len(text) > 0: if len(text) > 24: display_text = text[0:24] + "..." else: display_text = text self.ui.cbShowDataBitsOnly.setToolTip(text) self.ui.cbShowDataBitsOnly.setText( self.tr("Show Only Data Sequence\n") + "(" + display_text + ")") else: self.ui.cbShowDataBitsOnly.setToolTip("") self.ui.cbShowDataBitsOnly.setText( self.tr("Show Only Data Sequence\n")) self.search_data_sequence() if text == self.original_bits: self.ui.btnRestoreBits.setDisabled(True) else: self.ui.btnRestoreBits.setEnabled(True) for graphic_view in (self.ui.gVModulated, self.ui.gVData, self.ui.gVCarrier): graphic_view.show_full_scene(reinitialize=True) @pyqtSlot() def on_sample_rate_changed(self): if int(self.ui.spinBoxSampleRate.value()) > 0: self.current_modulator.sample_rate = int( self.ui.spinBoxSampleRate.value()) self.draw_carrier() self.draw_modulated() @pyqtSlot() def on_modulation_parameter_zero_changed(self): self.current_modulator.param_for_zero = self.ui.spinBoxParameter0.value( ) self.draw_modulated() @pyqtSlot() def on_modulation_parameter_one_changed(self): self.current_modulator.param_for_one = self.ui.spinBoxParameter1.value( ) self.draw_modulated() @pyqtSlot() def on_gauss_bt_changed(self): self.current_modulator.gauss_bt = self.ui.spinBoxGaussBT.value() self.draw_modulated() @pyqtSlot() def on_gaus_filter_wdith_changed(self): self.current_modulator.gauss_filter_width = self.ui.spinBoxGaussFilterWidth.value( ) self.draw_modulated() @pyqtSlot() def on_modulation_type_changed(self): if self.current_modulator.modulation_type_str == self.__cur_selected_mod_type( ): write_standard_parameters = False else: self.current_modulator.modulation_type_str = self.__cur_selected_mod_type( ) write_standard_parameters = True self.__set_gauss_ui_visibility( self.__cur_selected_mod_type() == "GFSK") if self.__cur_selected_mod_type() == "ASK": self.ui.lParameterfor0.setText(self.tr("Amplitude for 0:")) self.ui.lParameterfor1.setText(self.tr("Amplitude for 1:")) self.ui.spinBoxParameter0.setMaximum(100) self.ui.spinBoxParameter0.setMinimum(0) self.ui.spinBoxParameter0.setDecimals(0) self.ui.spinBoxParameter0.setSuffix("%") self.ui.spinBoxParameter1.setMaximum(100) self.ui.spinBoxParameter1.setMinimum(0) self.ui.spinBoxParameter1.setDecimals(0) self.ui.spinBoxParameter1.setSuffix("%") if write_standard_parameters: self.ui.spinBoxParameter0.setValue(0) self.ui.spinBoxParameter1.setValue(100) else: self.ui.spinBoxParameter0.setValue( self.current_modulator.param_for_zero) self.ui.spinBoxParameter1.setValue( self.current_modulator.param_for_one) elif self.__cur_selected_mod_type() in ("FSK", "GFSK"): self.ui.spinBoxParameter0.setSuffix("") self.ui.spinBoxParameter1.setSuffix("") self.ui.lParameterfor0.setText(self.tr("Frequency for 0:")) self.ui.lParameterfor1.setText(self.tr("Frequency for 1:")) self.ui.spinBoxParameter0.setMaximum(1e12) self.ui.spinBoxParameter0.setMinimum(-1e12) self.ui.spinBoxParameter0.setDecimals(4) self.ui.spinBoxParameter1.setMaximum(1e12) self.ui.spinBoxParameter1.setMinimum(-1e12) self.ui.spinBoxParameter1.setDecimals(4) if write_standard_parameters: self.autodetect_fsk_freqs() else: self.ui.spinBoxParameter0.setValue( self.current_modulator.param_for_zero) self.ui.spinBoxParameter1.setValue( self.current_modulator.param_for_one) elif self.__cur_selected_mod_type() == "PSK": self.ui.lParameterfor0.setText(self.tr("Phase (degree) for 0:")) self.ui.lParameterfor1.setText(self.tr("Phase (degree) for 1:")) self.ui.spinBoxParameter0.setMaximum(360) self.ui.spinBoxParameter0.setMinimum(-360) self.ui.spinBoxParameter0.setDecimals(0) self.ui.spinBoxParameter0.setSuffix("°") self.ui.spinBoxParameter1.setMaximum(360) self.ui.spinBoxParameter1.setMinimum(-360) self.ui.spinBoxParameter1.setDecimals(0) self.ui.spinBoxParameter1.setSuffix("°") if write_standard_parameters: self.ui.spinBoxParameter0.setValue(90) self.ui.spinBoxParameter1.setValue(270) else: self.ui.spinBoxParameter0.setValue( self.current_modulator.param_for_zero) self.ui.spinBoxParameter1.setValue( self.current_modulator.param_for_one) @pyqtSlot() def on_orig_signal_zoomed(self): start = self.ui.gVOriginalSignal.view_rect().x() end = start + self.ui.gVOriginalSignal.view_rect().width() self.ui.gVOriginalSignal.centerOn(start + (end - start) / 2, 0) if self.lock_samples_in_view: self.adjust_samples_in_view( self.ui.gVOriginalSignal.view_rect().width()) x = self.ui.gVOriginalSignal.view_rect().x( ) + self.ui.gVOriginalSignal.view_rect().width() / 2 y = 0 self.ui.gVModulated.centerOn(x, y) self.ui.gVCarrier.centerOn(x, y) self.ui.gVData.centerOn(x, y) else: self.mark_samples_in_view() @pyqtSlot(float) def on_carrier_data_modulated_zoomed(self, factor: float): x = self.sender().view_rect().x( ) + self.sender().view_rect().width() / 2 y = 0 for gv in (self.ui.gVCarrier, self.ui.gVData, self.ui.gVModulated): if gv == self.sender(): continue if factor == -1: gv.show_full_scene() else: gv.scale(factor, 1) gv.centerOn(x, y) if self.lock_samples_in_view: self.adjust_samples_in_view( self.ui.gVModulated.view_rect().width()) self.ui.gVOriginalSignal.centerOn(x, y) else: self.mark_samples_in_view() @pyqtSlot() def on_custom_modulation_name_edited(self): self.current_modulator.name = self.ui.comboBoxCustomModulations.currentText( ) @pyqtSlot() def on_custom_modulation_index_changed(self): self.set_ui_for_current_modulator() self.draw_carrier() self.draw_data_bits() self.draw_modulated() @pyqtSlot() def on_btn_next_search_result_clicked(self): cur_search_result = int(self.ui.lCurrentSearchResult.text()) - 1 self.show_search_result(cur_search_result + 1) @pyqtSlot() def on_btn_prev_search_result_clicked(self): cur_search_result = int(self.ui.lCurrentSearchResult.text()) - 1 self.show_search_result(cur_search_result - 1) @pyqtSlot() def on_show_data_bits_only_changed(self, redraw=True): show_data_bits_only = self.ui.cbShowDataBitsOnly.isChecked() if not self.ui.cbShowDataBitsOnly.isEnabled( ) or not show_data_bits_only: self.ui.btnSearchPrev.setEnabled(False) self.ui.btnSearchNext.setEnabled(False) self.ui.lCurrentSearchResult.setText("-") self.ui.lTotalSearchresults.setText("-") else: self.search_data_sequence() if not redraw: return if self.ui.cbShowDataBitsOnly.isEnabled() and not show_data_bits_only: self.draw_original_signal() @pyqtSlot() def on_remove_modulator_clicked(self): index = self.ui.comboBoxCustomModulations.currentIndex() self.ui.comboBoxCustomModulations.removeItem(index) self.modulators.remove(self.modulators[index]) if len(self.modulators) == 1: self.ui.btnRemoveModulation.setDisabled(True) @pyqtSlot() def on_lock_siv_changed(self): self.lock_samples_in_view = self.ui.chkBoxLockSIV.isChecked() if self.lock_samples_in_view: self.adjust_samples_in_view( self.ui.gVModulated.view_rect().width()) @pyqtSlot() def on_btn_restore_bits_clicked(self): self.ui.linEdDataBits.setText(self.original_bits) @pyqtSlot() def on_btn_autodetect_clicked(self): signal = self.ui.gVOriginalSignal.scene_manager.signal freq = self.current_modulator.estimate_carrier_frequency( signal, self.protocol) if freq is None or freq == 0: QMessageBox.information( self, self.tr("No results"), self.tr("Unable to detect parameters from current signal")) return self.ui.doubleSpinBoxCarrierFreq.setValue(freq) self.autodetect_fsk_freqs() @pyqtSlot(int) def on_modulated_selection_changed(self, new_width: int): self.ui.lModulatedSelectedSamples.setText(str(abs(new_width))) @pyqtSlot(int) def on_original_selection_changed(self, new_width: int): self.ui.lOriginalSignalSamplesSelected.setText(str(abs(new_width)))
class ModulatorDialog(QDialog): def __init__(self, modulators, tree_model=None, parent=None): """ :type modulators: list of Modulator """ super().__init__(parent) self.ui = Ui_DialogModulation() self.ui.setupUi(self) self.setAttribute(Qt.WA_DeleteOnClose) self.setWindowFlags(Qt.Window) self.lock_samples_in_view = False if tree_model is not None: self.ui.treeViewSignals.setModel(tree_model) self.ui.treeViewSignals.expandAll() self.ui.gVOriginalSignal.signal_tree_root = tree_model.rootItem self.ui.comboBoxCustomModulations.clear() for modulator in modulators: self.ui.comboBoxCustomModulations.addItem(modulator.name) if len(modulators) == 1: self.ui.btnRemoveModulation.setDisabled(True) self.modulators = modulators for graphic_view in (self.ui.gVCarrier, self.ui.gVData, self.ui.gVModulated): graphic_view.scene_y_min = -1 graphic_view.scene_y_max = 1 graphic_view.scene_x_zoom_stretch = 1.1 self.set_ui_for_current_modulator() self.ui.cbShowDataBitsOnly.setText(self.tr("Show Only Data Sequence\n")) self.ui.cbShowDataBitsOnly.setEnabled(False) self.protocol = None # type: ProtocolAnalyzer self.search_results = [] self.ui.cbShowDataBitsOnly.setEnabled(False) self.ui.btnSearchNext.setEnabled(False) self.ui.btnSearchPrev.setEnabled(False) self.ui.chkBoxLockSIV.setDisabled(True) self.original_bits = "" self.ui.btnRestoreBits.setEnabled(False) self.create_connects() try: self.restoreGeometry(constants.SETTINGS.value("{}/geometry".format(self.__class__.__name__))) except TypeError: pass self.set_modulation_profile_status() def __cur_selected_mod_type(self): s = self.ui.comboBoxModulationType.currentText() return s[s.rindex("(") + 1:s.rindex(")")] @staticmethod def __trim_number(number): if abs(number) >= 1e9: # giga return numpy.round(number / 1e9) * 1e9 elif abs(number) >= 1e6: # mega return numpy.round(number / 1e6) * 1e6 elif abs(number) >= 1e3: # Kilo return numpy.round(number / 1e3) * 1e3 else: return number @staticmethod def __ensure_multitude(num1, num2): try: if abs(num1) > abs(num2): num1 = abs(int(num1 / num2)) * num2 else: num2 = abs(int(num2 / num1)) * num1 return num1, num2 except Exception: return num1, num2 def __set_gauss_ui_visibility(self, show: bool): self.ui.lGaussBT.setVisible(show) self.ui.lGaussWidth.setVisible(show) self.ui.spinBoxGaussBT.setVisible(show) self.ui.spinBoxGaussFilterWidth.setVisible(show) self.ui.spinBoxGaussFilterWidth.setValue(self.current_modulator.gauss_filter_width) self.ui.spinBoxGaussBT.setValue(self.current_modulator.gauss_bt) def closeEvent(self, event: QCloseEvent): constants.SETTINGS.setValue("{}/geometry".format(self.__class__.__name__), self.saveGeometry()) for gv in (self.ui.gVCarrier, self.ui.gVData, self.ui.gVModulated, self.ui.gVOriginalSignal): # Eliminate graphic views to prevent segfaults gv.eliminate() super().closeEvent(event) @property def current_modulator(self): mod = self.modulators[self.ui.comboBoxCustomModulations.currentIndex()] return mod def set_ui_for_current_modulator(self): index = self.ui.comboBoxModulationType.findText("*(" + self.current_modulator.modulation_type_str + ")", Qt.MatchWildcard) self.ui.comboBoxModulationType.setCurrentIndex(index) self.ui.doubleSpinBoxCarrierFreq.setValue(self.current_modulator.carrier_freq_hz) self.ui.doubleSpinBoxCarrierPhase.setValue(self.current_modulator.carrier_phase_deg) self.ui.spinBoxBitLength.setValue(self.current_modulator.samples_per_bit) self.ui.spinBoxSampleRate.setValue(self.current_modulator.sample_rate) self.ui.spinBoxParameter0.setValue(self.current_modulator.param_for_zero) self.ui.spinBoxParameter1.setValue(self.current_modulator.param_for_one) def create_connects(self): self.ui.doubleSpinBoxCarrierFreq.valueChanged.connect(self.on_carrier_freq_changed) self.ui.doubleSpinBoxCarrierPhase.valueChanged.connect(self.on_carrier_phase_changed) self.ui.spinBoxBitLength.valueChanged.connect(self.on_bit_len_changed) self.ui.spinBoxSampleRate.valueChanged.connect(self.on_sample_rate_changed) self.ui.linEdDataBits.textChanged.connect(self.on_data_bits_changed) self.ui.spinBoxParameter0.valueChanged.connect(self.on_modulation_parameter_zero_changed) self.ui.spinBoxParameter1.valueChanged.connect(self.on_modulation_parameter_one_changed) self.ui.comboBoxModulationType.currentIndexChanged.connect(self.on_modulation_type_changed) self.ui.gVOriginalSignal.zoomed.connect(self.on_orig_signal_zoomed) self.ui.cbShowDataBitsOnly.stateChanged.connect(self.on_show_data_bits_only_changed) self.ui.btnSearchNext.clicked.connect(self.on_btn_next_search_result_clicked) self.ui.btnSearchPrev.clicked.connect(self.on_btn_prev_search_result_clicked) self.ui.comboBoxCustomModulations.editTextChanged.connect(self.on_custom_modulation_name_edited) self.ui.comboBoxCustomModulations.currentIndexChanged.connect(self.on_custom_modulation_index_changed) self.ui.btnAddModulation.clicked.connect(self.add_modulator) self.ui.btnRemoveModulation.clicked.connect(self.on_remove_modulator_clicked) self.ui.gVModulated.zoomed.connect(self.on_carrier_data_modulated_zoomed) self.ui.gVCarrier.zoomed.connect(self.on_carrier_data_modulated_zoomed) self.ui.gVData.zoomed.connect(self.on_carrier_data_modulated_zoomed) self.ui.gVModulated.selection_width_changed.connect(self.on_modulated_selection_changed) self.ui.gVOriginalSignal.selection_width_changed.connect(self.on_original_selection_changed) self.ui.spinBoxGaussBT.valueChanged.connect(self.on_gauss_bt_changed) self.ui.spinBoxGaussFilterWidth.valueChanged.connect(self.on_gaus_filter_wdith_changed) self.ui.chkBoxLockSIV.stateChanged.connect(self.on_lock_siv_changed) self.ui.btnRestoreBits.clicked.connect(self.on_btn_restore_bits_clicked) self.ui.gVOriginalSignal.signal_loaded.connect(self.handle_signal_loaded) self.ui.btnAutoDetect.clicked.connect(self.on_btn_autodetect_clicked) def draw_carrier(self): self.ui.gVCarrier.plot_data(self.current_modulator.carrier_data) def draw_data_bits(self): self.ui.gVData.setScene(self.current_modulator.data_scene) self.ui.gVData.update() def draw_modulated(self): self.ui.gVModulated.plot_data(self.current_modulator.modulate(pause=0).imag.astype(numpy.float32)) if self.lock_samples_in_view: siv = self.ui.gVOriginalSignal.view_rect().width() self.adjust_samples_in_view(siv) else: self.mark_samples_in_view() def draw_original_signal(self, start=0, end=-1): scene_manager = self.ui.gVOriginalSignal.scene_manager if scene_manager is None: return if end == -1: end = scene_manager.signal.num_samples y = scene_manager.scene.sceneRect().y() h = scene_manager.scene.sceneRect().height() self.ui.gVOriginalSignal.setSceneRect(start, y, end - start, h) self.ui.gVOriginalSignal.fitInView(self.ui.gVOriginalSignal.sceneRect()) scene_manager.show_scene_section(start, end) self.ui.gVOriginalSignal.update() if self.lock_samples_in_view: self.adjust_samples_in_view(self.ui.gVModulated.view_rect().width()) else: self.mark_samples_in_view() def update_views(self): self.ui.gVCarrier.update() self.ui.gVData.update() self.ui.gVModulated.update() self.ui.gVOriginalSignal.update() def search_data_sequence(self): if not self.ui.cbShowDataBitsOnly.isEnabled() or not self.ui.cbShowDataBitsOnly.isChecked(): return search_seq = self.ui.linEdDataBits.text() if len(search_seq) == 0 or self.protocol is None: return self.search_results[:] = [] proto_bits = self.protocol.plain_bits_str len_seq = len(search_seq) for i, message in enumerate(proto_bits): j = message.find(search_seq) while j != -1: self.search_results.append((i, j, j + len_seq)) j = message.find(search_seq, j + 1) self.ui.lTotalSearchresults.setText(str(len(self.search_results))) self.show_search_result(0) def show_search_result(self, i: int): if len(self.search_results) == 0: self.ui.lCurrentSearchResult.setText("0") self.ui.gVOriginalSignal.scene_manager.clear_path() return message, start_index, end_index = self.search_results[i] start, nsamples = self.protocol.get_samplepos_of_bitseq(message, start_index, message, end_index, False) self.draw_original_signal(start=start, end=start + nsamples) self.ui.lCurrentSearchResult.setText(str(i + 1)) if i == len(self.search_results) - 1: self.ui.btnSearchNext.setEnabled(False) else: self.ui.btnSearchNext.setEnabled(True) if i == 0: self.ui.btnSearchPrev.setEnabled(False) else: self.ui.btnSearchPrev.setEnabled(True) def add_modulator(self): names = [m.name for m in self.modulators] name = "Modulation" number = 1 while name in names: name = "Modulation " + str(number) number += 1 self.modulators.append(Modulator(name)) self.ui.comboBoxCustomModulations.addItem(name) self.ui.comboBoxCustomModulations.setCurrentIndex(len(self.modulators) - 1) self.ui.btnRemoveModulation.setEnabled(True) def adjust_samples_in_view(self, target_siv: float): self.ui.gVOriginalSignal.scale(self.ui.gVOriginalSignal.view_rect().width() / target_siv, 1) mod_zoom_factor = self.ui.gVModulated.view_rect().width() / target_siv self.ui.gVModulated.scale(mod_zoom_factor, 1) self.ui.gVCarrier.scale(mod_zoom_factor, 1) self.ui.gVData.scale(mod_zoom_factor, 1) self.mark_samples_in_view() def autodetect_fsk_freqs(self): if self.__cur_selected_mod_type() not in ("FSK", "GFSK"): return try: zero_freq = self.protocol.estimate_frequency_for_zero(self.current_modulator.sample_rate) one_freq = self.protocol.estimate_frequency_for_one(self.current_modulator.sample_rate) zero_freq = self.__trim_number(zero_freq) one_freq = self.__trim_number(one_freq) zero_freq, one_freq = self.__ensure_multitude(zero_freq, one_freq) if zero_freq == one_freq: # If frequencies are equal, it is very probable the zero freq is negative zero_freq = -one_freq self.ui.spinBoxParameter0.setValue(zero_freq) self.ui.spinBoxParameter1.setValue(one_freq) except AttributeError: self.ui.spinBoxParameter0.setValue(self.current_modulator.carrier_freq_hz / 2) self.ui.spinBoxParameter1.setValue(self.current_modulator.carrier_freq_hz) def handle_signal_loaded(self, protocol): self.setCursor(Qt.WaitCursor) self.ui.cbShowDataBitsOnly.setEnabled(True) self.ui.chkBoxLockSIV.setEnabled(True) self.ui.btnAutoDetect.setEnabled(True) self.protocol = protocol # Apply bit length of original signal to current modulator self.ui.spinBoxBitLength.setValue(self.ui.gVOriginalSignal.signal.bit_len) # https://github.com/jopohl/urh/issues/130 self.ui.gVModulated.show_full_scene(reinitialize=True) self.ui.gVCarrier.show_full_scene(reinitialize=True) self.ui.gVData.show_full_scene(reinitialize=True) self.unsetCursor() def mark_samples_in_view(self): self.ui.lSamplesInViewModulated.setText(str(int(self.ui.gVModulated.view_rect().width()))) if self.ui.gVOriginalSignal.scene_manager is not None: self.ui.lSamplesInViewOrigSignal.setText(str(int(self.ui.gVOriginalSignal.view_rect().width()))) else: self.ui.lSamplesInViewOrigSignal.setText("-") return if int(self.ui.gVOriginalSignal.view_rect().width()) != int(self.ui.gVModulated.view_rect().width()): font = self.ui.lSamplesInViewModulated.font() font.setBold(False) self.ui.lSamplesInViewModulated.setFont(font) self.ui.lSamplesInViewOrigSignal.setFont(font) self.ui.lSamplesInViewOrigSignal.setStyleSheet("QLabel { color : red; }") self.ui.lSamplesInViewModulated.setStyleSheet("QLabel { color : red; }") else: font = self.ui.lSamplesInViewModulated.font() font.setBold(True) self.ui.lSamplesInViewModulated.setFont(font) self.ui.lSamplesInViewOrigSignal.setFont(font) self.ui.lSamplesInViewOrigSignal.setStyleSheet("") self.ui.lSamplesInViewModulated.setStyleSheet("") def set_modulation_profile_status(self): visible = constants.SETTINGS.value("multiple_modulations", False, bool) self.ui.btnAddModulation.setVisible(visible) self.ui.btnRemoveModulation.setVisible(visible) self.ui.comboBoxCustomModulations.setVisible(visible) def resizeEvent(self, event: QResizeEvent): self.update_views() def keyPressEvent(self, event: QKeyEvent): if event.key() == Qt.Key_Enter or event.key() == Qt.Key_Return: return else: super().keyPressEvent(event) def initialize(self, bits: str): self.on_modulation_type_changed() # for drawing modulated signal initially self.original_bits = bits self.ui.linEdDataBits.setText(bits) self.draw_original_signal() self.ui.gVModulated.show_full_scene(reinitialize=True) self.ui.gVModulated.auto_fit_view() self.ui.gVData.show_full_scene(reinitialize=True) self.ui.gVData.auto_fit_view() self.ui.gVCarrier.show_full_scene(reinitialize=True) self.ui.gVCarrier.auto_fit_view() self.mark_samples_in_view() @pyqtSlot() def on_carrier_freq_changed(self): self.current_modulator.carrier_freq_hz = self.ui.doubleSpinBoxCarrierFreq.value() self.draw_carrier() self.draw_modulated() @pyqtSlot() def on_carrier_phase_changed(self): self.current_modulator.carrier_phase_deg = self.ui.doubleSpinBoxCarrierPhase.value() self.draw_carrier() self.draw_modulated() @pyqtSlot() def on_bit_len_changed(self): self.current_modulator.samples_per_bit = self.ui.spinBoxBitLength.value() self.draw_carrier() self.draw_data_bits() self.draw_modulated() for graphic_view in (self.ui.gVModulated, self.ui.gVData, self.ui.gVCarrier): graphic_view.show_full_scene(reinitialize=True) @pyqtSlot() def on_data_bits_changed(self): text = self.ui.linEdDataBits.text() text = ''.join(c for c in text if c == "1" or c == "0") self.ui.linEdDataBits.blockSignals(True) self.ui.linEdDataBits.setText(text) self.ui.linEdDataBits.blockSignals(False) self.current_modulator.display_bits = text self.draw_carrier() self.draw_data_bits() self.draw_modulated() if len(text) > 0: if len(text) > 24: display_text = text[0:24] + "..." else: display_text = text self.ui.cbShowDataBitsOnly.setToolTip(text) self.ui.cbShowDataBitsOnly.setText(self.tr("Show Only Data Sequence\n") + "(" + display_text + ")") else: self.ui.cbShowDataBitsOnly.setToolTip("") self.ui.cbShowDataBitsOnly.setText(self.tr("Show Only Data Sequence\n")) self.search_data_sequence() if text == self.original_bits: self.ui.btnRestoreBits.setDisabled(True) else: self.ui.btnRestoreBits.setEnabled(True) for graphic_view in (self.ui.gVModulated, self.ui.gVData, self.ui.gVCarrier): graphic_view.show_full_scene(reinitialize=True) @pyqtSlot() def on_sample_rate_changed(self): if int(self.ui.spinBoxSampleRate.value()) > 0: self.current_modulator.sample_rate = int(self.ui.spinBoxSampleRate.value()) self.draw_carrier() self.draw_modulated() @pyqtSlot() def on_modulation_parameter_zero_changed(self): self.current_modulator.param_for_zero = self.ui.spinBoxParameter0.value() self.draw_modulated() @pyqtSlot() def on_modulation_parameter_one_changed(self): self.current_modulator.param_for_one = self.ui.spinBoxParameter1.value() self.draw_modulated() @pyqtSlot() def on_gauss_bt_changed(self): self.current_modulator.gauss_bt = self.ui.spinBoxGaussBT.value() self.draw_modulated() @pyqtSlot() def on_gaus_filter_wdith_changed(self): self.current_modulator.gauss_filter_width = self.ui.spinBoxGaussFilterWidth.value() self.draw_modulated() @pyqtSlot() def on_modulation_type_changed(self): if self.current_modulator.modulation_type_str == self.__cur_selected_mod_type(): write_standard_parameters = False else: self.current_modulator.modulation_type_str = self.__cur_selected_mod_type() write_standard_parameters = True self.__set_gauss_ui_visibility(self.__cur_selected_mod_type() == "GFSK") if self.__cur_selected_mod_type() == "ASK": self.ui.lParameterfor0.setText(self.tr("Amplitude for 0:")) self.ui.lParameterfor1.setText(self.tr("Amplitude for 1:")) self.ui.spinBoxParameter0.setMaximum(100) self.ui.spinBoxParameter0.setMinimum(0) self.ui.spinBoxParameter0.setDecimals(0) self.ui.spinBoxParameter0.setSuffix("%") self.ui.spinBoxParameter1.setMaximum(100) self.ui.spinBoxParameter1.setMinimum(0) self.ui.spinBoxParameter1.setDecimals(0) self.ui.spinBoxParameter1.setSuffix("%") if write_standard_parameters: self.ui.spinBoxParameter0.setValue(0) self.ui.spinBoxParameter1.setValue(100) else: self.ui.spinBoxParameter0.setValue(self.current_modulator.param_for_zero) self.ui.spinBoxParameter1.setValue(self.current_modulator.param_for_one) elif self.__cur_selected_mod_type() in ("FSK", "GFSK"): self.ui.spinBoxParameter0.setSuffix("") self.ui.spinBoxParameter1.setSuffix("") self.ui.lParameterfor0.setText(self.tr("Frequency for 0:")) self.ui.lParameterfor1.setText(self.tr("Frequency for 1:")) self.ui.spinBoxParameter0.setMaximum(1e12) self.ui.spinBoxParameter0.setMinimum(-1e12) self.ui.spinBoxParameter0.setDecimals(4) self.ui.spinBoxParameter1.setMaximum(1e12) self.ui.spinBoxParameter1.setMinimum(-1e12) self.ui.spinBoxParameter1.setDecimals(4) if write_standard_parameters: self.autodetect_fsk_freqs() else: self.ui.spinBoxParameter0.setValue(self.current_modulator.param_for_zero) self.ui.spinBoxParameter1.setValue(self.current_modulator.param_for_one) elif self.__cur_selected_mod_type() == "PSK": self.ui.lParameterfor0.setText(self.tr("Phase (degree) for 0:")) self.ui.lParameterfor1.setText(self.tr("Phase (degree) for 1:")) self.ui.spinBoxParameter0.setMaximum(360) self.ui.spinBoxParameter0.setMinimum(-360) self.ui.spinBoxParameter0.setDecimals(0) self.ui.spinBoxParameter0.setSuffix("°") self.ui.spinBoxParameter1.setMaximum(360) self.ui.spinBoxParameter1.setMinimum(-360) self.ui.spinBoxParameter1.setDecimals(0) self.ui.spinBoxParameter1.setSuffix("°") if write_standard_parameters: self.ui.spinBoxParameter0.setValue(0) self.ui.spinBoxParameter1.setValue(180) else: self.ui.spinBoxParameter0.setValue(self.current_modulator.param_for_zero) self.ui.spinBoxParameter1.setValue(self.current_modulator.param_for_one) @pyqtSlot() def on_orig_signal_zoomed(self): start = self.ui.gVOriginalSignal.view_rect().x() end = start + self.ui.gVOriginalSignal.view_rect().width() self.ui.gVOriginalSignal.centerOn(start + (end - start) / 2, 0) if self.lock_samples_in_view: self.adjust_samples_in_view(self.ui.gVOriginalSignal.view_rect().width()) x = self.ui.gVOriginalSignal.view_rect().x() + self.ui.gVOriginalSignal.view_rect().width() / 2 y = 0 self.ui.gVModulated.centerOn(x, y) self.ui.gVCarrier.centerOn(x, y) self.ui.gVData.centerOn(x, y) else: self.mark_samples_in_view() @pyqtSlot(float) def on_carrier_data_modulated_zoomed(self, factor: float): x = self.sender().view_rect().x() + self.sender().view_rect().width() / 2 y = 0 for gv in (self.ui.gVCarrier, self.ui.gVData, self.ui.gVModulated): if gv == self.sender(): continue if factor == -1: gv.show_full_scene() else: gv.scale(factor, 1) gv.centerOn(x, y) if self.lock_samples_in_view: self.adjust_samples_in_view(self.ui.gVModulated.view_rect().width()) self.ui.gVOriginalSignal.centerOn(x, y) else: self.mark_samples_in_view() @pyqtSlot() def on_custom_modulation_name_edited(self): self.current_modulator.name = self.ui.comboBoxCustomModulations.currentText() @pyqtSlot() def on_custom_modulation_index_changed(self): self.set_ui_for_current_modulator() self.draw_carrier() self.draw_data_bits() self.draw_modulated() @pyqtSlot() def on_btn_next_search_result_clicked(self): cur_search_result = int(self.ui.lCurrentSearchResult.text()) - 1 self.show_search_result(cur_search_result + 1) @pyqtSlot() def on_btn_prev_search_result_clicked(self): cur_search_result = int(self.ui.lCurrentSearchResult.text()) - 1 self.show_search_result(cur_search_result - 1) @pyqtSlot() def on_show_data_bits_only_changed(self, redraw=True): show_data_bits_only = self.ui.cbShowDataBitsOnly.isChecked() if not self.ui.cbShowDataBitsOnly.isEnabled() or not show_data_bits_only: self.ui.btnSearchPrev.setEnabled(False) self.ui.btnSearchNext.setEnabled(False) self.ui.lCurrentSearchResult.setText("-") self.ui.lTotalSearchresults.setText("-") else: self.search_data_sequence() if not redraw: return if self.ui.cbShowDataBitsOnly.isEnabled() and not show_data_bits_only: self.draw_original_signal() @pyqtSlot() def on_remove_modulator_clicked(self): index = self.ui.comboBoxCustomModulations.currentIndex() self.ui.comboBoxCustomModulations.removeItem(index) self.modulators.remove(self.modulators[index]) if len(self.modulators) == 1: self.ui.btnRemoveModulation.setDisabled(True) @pyqtSlot() def on_lock_siv_changed(self): self.lock_samples_in_view = self.ui.chkBoxLockSIV.isChecked() if self.lock_samples_in_view: self.adjust_samples_in_view(self.ui.gVModulated.view_rect().width()) @pyqtSlot() def on_btn_restore_bits_clicked(self): self.ui.linEdDataBits.setText(self.original_bits) @pyqtSlot() def on_btn_autodetect_clicked(self): signal = self.ui.gVOriginalSignal.scene_manager.signal freq = self.current_modulator.estimate_carrier_frequency(signal, self.protocol) if freq is None or freq == 0: QMessageBox.information(self, self.tr("No results"), self.tr("Unable to detect parameters from current signal")) return self.ui.doubleSpinBoxCarrierFreq.setValue(freq) self.autodetect_fsk_freqs() @pyqtSlot(int) def on_modulated_selection_changed(self, new_width: int): self.ui.lModulatedSelectedSamples.setText(str(abs(new_width))) @pyqtSlot(int) def on_original_selection_changed(self, new_width: int): self.ui.lOriginalSignalSamplesSelected.setText(str(abs(new_width)))
class ModulatorDialog(QDialog): def __init__(self, modulators, tree_model=None, parent=None): """ :type modulators: list of Modulator """ super().__init__(parent) self.ui = Ui_DialogModulation() self.ui.setupUi(self) self.setAttribute(Qt.WA_DeleteOnClose) self.setWindowFlags(Qt.Window) self.lock_samples_in_view = False if tree_model is not None: self.ui.treeViewSignals.setModel(tree_model) self.ui.treeViewSignals.expandAll() self.ui.gVOriginalSignal.signal_tree_root = tree_model.rootItem self.ui.comboBoxCustomModulations.clear() for modulator in modulators: self.ui.comboBoxCustomModulations.addItem(modulator.name) if len(modulators) == 1: self.ui.btnRemoveModulation.setDisabled(True) self.modulators = modulators for graphic_view in (self.ui.gVCarrier, self.ui.gVData): graphic_view.scene_y_min = -1 graphic_view.scene_y_max = 1 graphic_view.scene_x_zoom_stretch = 1.1 min_max_mod = IQArray.min_max_for_dtype( self.current_modulator.get_dtype()) self.ui.gVModulated.scene_y_min = min_max_mod[0] self.ui.gVModulated.scene_y_max = min_max_mod[1] self.ui.gVModulated.scene_x_zoom_stretch = 1.1 self.set_ui_for_current_modulator() self.ui.cbShowDataBitsOnly.setText( self.tr("Show Only Data Sequence\n")) self.ui.cbShowDataBitsOnly.setEnabled(False) self.protocol = None # type: ProtocolAnalyzer self.search_results = [] self.ui.cbShowDataBitsOnly.setEnabled(False) self.ui.btnSearchNext.setEnabled(False) self.ui.btnSearchPrev.setEnabled(False) self.ui.chkBoxLockSIV.setDisabled(True) self.original_bits = "" self.restore_bits_action = self.ui.linEdDataBits.addAction( QIcon.fromTheme("edit-undo"), QLineEdit.TrailingPosition) self.restore_bits_action.setEnabled(False) self.configure_parameters_action = self.ui.lineEditParameters.addAction( QIcon.fromTheme("configure"), QLineEdit.TrailingPosition) self.create_connects() try: self.restoreGeometry( settings.read("{}/geometry".format(self.__class__.__name__))) except TypeError: pass self.set_bits_per_symbol_enabled_status() self.set_modulation_profile_status() # Ensure full srceen shown after resize QTimer.singleShot(100, self.show_full_scene) def __cur_selected_mod_type(self): s = self.ui.comboBoxModulationType.currentText() return s[s.rindex("(") + 1:s.rindex(")")] @staticmethod def __trim_number(number): if abs(number) >= 1e9: # giga return numpy.round(number / 1e9) * 1e9 elif abs(number) >= 1e6: # mega return numpy.round(number / 1e6) * 1e6 elif abs(number) >= 1e3: # Kilo return numpy.round(number / 1e3) * 1e3 else: return number @staticmethod def __ensure_multitude(num1, num2): try: if abs(num1) > abs(num2): num1 = abs(int(num1 / num2)) * num2 else: num2 = abs(int(num2 / num1)) * num1 return num1, num2 except Exception: return num1, num2 def __set_gauss_ui_visibility(self, show: bool): self.ui.lGaussBT.setVisible(show) self.ui.lGaussWidth.setVisible(show) self.ui.spinBoxGaussBT.setVisible(show) self.ui.spinBoxGaussFilterWidth.setVisible(show) self.ui.spinBoxGaussFilterWidth.setValue( self.current_modulator.gauss_filter_width) self.ui.spinBoxGaussBT.setValue(self.current_modulator.gauss_bt) def closeEvent(self, event: QCloseEvent): self.ui.lineEditParameters.editingFinished.emit() settings.write("{}/geometry".format(self.__class__.__name__), self.saveGeometry()) for gv in (self.ui.gVCarrier, self.ui.gVData, self.ui.gVModulated, self.ui.gVOriginalSignal): # Eliminate graphic views to prevent segfaults gv.eliminate() super().closeEvent(event) @property def current_modulator(self): mod = self.modulators[self.ui.comboBoxCustomModulations.currentIndex()] return mod def set_ui_for_current_modulator(self): index = self.ui.comboBoxModulationType.findText( "*(" + self.current_modulator.modulation_type + ")", Qt.MatchWildcard) self.ui.comboBoxModulationType.setCurrentIndex(index) self.ui.doubleSpinBoxCarrierFreq.setValue( self.current_modulator.carrier_freq_hz) self.ui.doubleSpinBoxCarrierPhase.setValue( self.current_modulator.carrier_phase_deg) self.ui.spinBoxSamplesPerSymbol.setValue( self.current_modulator.samples_per_symbol) self.ui.spinBoxSampleRate.setValue(self.current_modulator.sample_rate) self.ui.spinBoxBitsPerSymbol.setValue( self.current_modulator.bits_per_symbol) self.update_modulation_parameters() def create_connects(self): self.ui.doubleSpinBoxCarrierFreq.valueChanged.connect( self.on_carrier_freq_changed) self.ui.doubleSpinBoxCarrierPhase.valueChanged.connect( self.on_carrier_phase_changed) self.ui.spinBoxSamplesPerSymbol.valueChanged.connect( self.on_samples_per_symbol_changed) self.ui.spinBoxSampleRate.valueChanged.connect( self.on_sample_rate_changed) self.ui.linEdDataBits.textChanged.connect(self.on_data_bits_changed) self.ui.spinBoxBitsPerSymbol.valueChanged.connect( self.on_bits_per_symbol_changed) self.ui.comboBoxModulationType.currentIndexChanged.connect( self.on_modulation_type_changed) self.ui.gVOriginalSignal.zoomed.connect(self.on_orig_signal_zoomed) self.ui.cbShowDataBitsOnly.stateChanged.connect( self.on_show_data_bits_only_changed) self.ui.btnSearchNext.clicked.connect( self.on_btn_next_search_result_clicked) self.ui.btnSearchPrev.clicked.connect( self.on_btn_prev_search_result_clicked) self.ui.comboBoxCustomModulations.editTextChanged.connect( self.on_custom_modulation_name_edited) self.ui.comboBoxCustomModulations.currentIndexChanged.connect( self.on_custom_modulation_index_changed) self.ui.btnAddModulation.clicked.connect(self.add_modulator) self.ui.btnRemoveModulation.clicked.connect( self.on_remove_modulator_clicked) self.ui.gVModulated.zoomed.connect( self.on_carrier_data_modulated_zoomed) self.ui.gVCarrier.zoomed.connect(self.on_carrier_data_modulated_zoomed) self.ui.gVData.zoomed.connect(self.on_carrier_data_modulated_zoomed) self.ui.gVModulated.selection_width_changed.connect( self.on_modulated_selection_changed) self.ui.gVOriginalSignal.selection_width_changed.connect( self.on_original_selection_changed) self.ui.spinBoxGaussBT.valueChanged.connect(self.on_gauss_bt_changed) self.ui.spinBoxGaussFilterWidth.valueChanged.connect( self.on_gauss_filter_width_changed) self.ui.chkBoxLockSIV.stateChanged.connect(self.on_lock_siv_changed) self.ui.gVOriginalSignal.signal_loaded.connect( self.handle_signal_loaded) self.ui.btnAutoDetect.clicked.connect(self.on_btn_autodetect_clicked) self.restore_bits_action.triggered.connect( self.on_restore_bits_action_triggered) self.configure_parameters_action.triggered.connect( self.on_configure_parameters_action_triggered) self.ui.lineEditParameters.editingFinished.connect( self.on_line_edit_parameters_editing_finished) def draw_carrier(self): self.ui.gVCarrier.plot_data(self.current_modulator.carrier_data) def draw_data_bits(self): self.ui.gVData.setScene(self.current_modulator.data_scene) self.ui.gVData.update() def draw_modulated(self): self.ui.gVModulated.plot_data( self.current_modulator.modulate(pause=0).imag.astype( numpy.float32)) if self.lock_samples_in_view: siv = self.ui.gVOriginalSignal.view_rect().width() self.adjust_samples_in_view(siv) else: self.mark_samples_in_view() def draw_original_signal(self, start=0, end=-1): scene_manager = self.ui.gVOriginalSignal.scene_manager if scene_manager is None: return if end == -1: end = scene_manager.signal.num_samples y = scene_manager.scene.sceneRect().y() h = scene_manager.scene.sceneRect().height() self.ui.gVOriginalSignal.setSceneRect(start, y, end - start, h) self.ui.gVOriginalSignal.fitInView( self.ui.gVOriginalSignal.sceneRect()) scene_manager.show_scene_section(start, end) self.ui.gVOriginalSignal.update() if self.lock_samples_in_view: self.adjust_samples_in_view( self.ui.gVModulated.view_rect().width()) else: self.mark_samples_in_view() def update_views(self): self.ui.gVCarrier.update() self.ui.gVData.update() self.ui.gVModulated.update() self.ui.gVOriginalSignal.update() def search_data_sequence(self): if not self.ui.cbShowDataBitsOnly.isEnabled( ) or not self.ui.cbShowDataBitsOnly.isChecked(): return search_seq = self.ui.linEdDataBits.text() if len(search_seq) == 0 or self.protocol is None: return self.search_results[:] = [] proto_bits = self.protocol.plain_bits_str len_seq = len(search_seq) for i, message in enumerate(proto_bits): j = message.find(search_seq) while j != -1: self.search_results.append((i, j, j + len_seq)) j = message.find(search_seq, j + 1) self.ui.lTotalSearchresults.setText(str(len(self.search_results))) self.show_search_result(0) def show_search_result(self, i: int): if len(self.search_results) == 0: self.ui.lCurrentSearchResult.setText("0") self.ui.gVOriginalSignal.scene_manager.clear_path() return message, start_index, end_index = self.search_results[i] start, nsamples = self.protocol.get_samplepos_of_bitseq( message, start_index, message, end_index, False) self.draw_original_signal(start=start, end=start + nsamples) self.ui.lCurrentSearchResult.setText(str(i + 1)) self.ui.btnSearchNext.setEnabled(i != len(self.search_results) - 1) self.ui.btnSearchPrev.setEnabled(i > 0) def add_modulator(self): names = [m.name for m in self.modulators] name = "Modulation" number = 1 while name in names: name = "Modulation " + str(number) number += 1 self.modulators.append(Modulator(name)) self.ui.comboBoxCustomModulations.addItem(name) self.ui.comboBoxCustomModulations.setCurrentIndex( len(self.modulators) - 1) self.ui.btnRemoveModulation.setEnabled(True) def adjust_samples_in_view(self, target_siv: float): self.ui.gVOriginalSignal.scale( self.ui.gVOriginalSignal.view_rect().width() / target_siv, 1) mod_zoom_factor = self.ui.gVModulated.view_rect().width() / target_siv self.ui.gVModulated.scale(mod_zoom_factor, 1) self.ui.gVCarrier.scale(mod_zoom_factor, 1) self.ui.gVData.scale(mod_zoom_factor, 1) self.mark_samples_in_view() def detect_fsk_frequencies(self): if not self.current_modulator.is_frequency_based: return frequencies = [] try: if not self.current_modulator.is_binary_modulation: raise NotImplementedError() zero_freq = self.protocol.estimate_frequency_for_zero( self.current_modulator.sample_rate) one_freq = self.protocol.estimate_frequency_for_one( self.current_modulator.sample_rate) zero_freq = self.__trim_number(zero_freq) one_freq = self.__trim_number(one_freq) zero_freq, one_freq = self.__ensure_multitude(zero_freq, one_freq) if zero_freq == one_freq: # If frequencies are equal, it is very likely the zero freq is negative zero_freq = -one_freq frequencies = [zero_freq, one_freq] except (AttributeError, NotImplementedError): frequencies = self.current_modulator.get_default_parameters() self.current_modulator.parameters = array("f", frequencies) self.update_modulation_parameters() def handle_signal_loaded(self, protocol): self.setCursor(Qt.WaitCursor) self.ui.cbShowDataBitsOnly.setEnabled(True) self.ui.chkBoxLockSIV.setEnabled(True) self.ui.btnAutoDetect.setEnabled(True) self.protocol = protocol # Apply bit length of original signal to current modulator self.ui.spinBoxSamplesPerSymbol.setValue( self.ui.gVOriginalSignal.signal.samples_per_symbol) # https://github.com/jopohl/urh/issues/130 self.ui.gVModulated.show_full_scene(reinitialize=True) self.ui.gVCarrier.show_full_scene(reinitialize=True) self.ui.gVData.show_full_scene(reinitialize=True) self.unsetCursor() def mark_samples_in_view(self): self.ui.lSamplesInViewModulated.setText( str(int(self.ui.gVModulated.view_rect().width()))) if self.ui.gVOriginalSignal.scene_manager is not None: self.ui.lSamplesInViewOrigSignal.setText( str(int(self.ui.gVOriginalSignal.view_rect().width()))) else: self.ui.lSamplesInViewOrigSignal.setText("-") return if int(self.ui.gVOriginalSignal.view_rect().width()) != int( self.ui.gVModulated.view_rect().width()): font = self.ui.lSamplesInViewModulated.font() font.setBold(False) self.ui.lSamplesInViewModulated.setFont(font) self.ui.lSamplesInViewOrigSignal.setFont(font) self.ui.lSamplesInViewOrigSignal.setStyleSheet( "QLabel { color : red; }") self.ui.lSamplesInViewModulated.setStyleSheet( "QLabel { color : red; }") else: font = self.ui.lSamplesInViewModulated.font() font.setBold(True) self.ui.lSamplesInViewModulated.setFont(font) self.ui.lSamplesInViewOrigSignal.setFont(font) self.ui.lSamplesInViewOrigSignal.setStyleSheet("") self.ui.lSamplesInViewModulated.setStyleSheet("") def set_default_modulation_parameters(self): self.current_modulator.parameters = self.current_modulator.get_default_parameters( ) self.update_modulation_parameters() def set_modulation_profile_status(self): visible = settings.read("multiple_modulations", False, bool) self.ui.btnAddModulation.setVisible(visible) self.ui.btnRemoveModulation.setVisible(visible) self.ui.comboBoxCustomModulations.setVisible(visible) def resizeEvent(self, event: QResizeEvent): self.update_views() def keyPressEvent(self, event: QKeyEvent): if event.key() == Qt.Key_Enter or event.key() == Qt.Key_Return: return else: super().keyPressEvent(event) def initialize(self, bits: str): self.on_modulation_type_changed( ) # for drawing modulated signal initially self.original_bits = bits self.ui.linEdDataBits.setText(bits) self.draw_original_signal() self.ui.gVModulated.show_full_scene(reinitialize=True) self.ui.gVModulated.auto_fit_view() self.ui.gVData.show_full_scene(reinitialize=True) self.ui.gVData.auto_fit_view() self.ui.gVCarrier.show_full_scene(reinitialize=True) self.ui.gVCarrier.auto_fit_view() self.mark_samples_in_view() def update_modulation_parameters(self): n = len(self.current_modulator.parameters) - 1 if self.current_modulator.is_amplitude_based: regex = r"(100|[0-9]{1,2})" elif self.current_modulator.is_frequency_based: regex = r"((-?[0-9]+)[.,]?[0-9]*[kKmMgG]?)" elif self.current_modulator.is_phase_based: regex = r"(-?(36[0]|3[0-5][0-9]|[12][0-9][0-9]|[1-9]?[0-9]))" else: raise ValueError("Unknown modulation type") full_regex = r"^(" + regex + r"/){" + str(n) + "}" + regex + r"$" self.ui.lineEditParameters.setValidator( QRegExpValidator(QRegExp(full_regex))) self.ui.lineEditParameters.setText( self.current_modulator.parameters_string) def set_bits_per_symbol_enabled_status(self): if self.current_modulator.modulation_type == "OQPSK": self.ui.spinBoxBitsPerSymbol.setEnabled(False) self.ui.spinBoxBitsPerSymbol.setValue(2) else: self.ui.spinBoxBitsPerSymbol.setEnabled(True) def show_full_scene(self): for graphic_view in (self.ui.gVModulated, self.ui.gVData, self.ui.gVCarrier): graphic_view.show_full_scene(reinitialize=True) @pyqtSlot() def on_carrier_freq_changed(self): self.current_modulator.carrier_freq_hz = self.ui.doubleSpinBoxCarrierFreq.value( ) self.draw_carrier() self.draw_modulated() @pyqtSlot() def on_carrier_phase_changed(self): self.current_modulator.carrier_phase_deg = self.ui.doubleSpinBoxCarrierPhase.value( ) self.draw_carrier() self.draw_modulated() @pyqtSlot() def on_samples_per_symbol_changed(self): self.current_modulator.samples_per_symbol = self.ui.spinBoxSamplesPerSymbol.value( ) self.draw_carrier() self.draw_data_bits() self.draw_modulated() self.show_full_scene() @pyqtSlot() def on_data_bits_changed(self): text = self.ui.linEdDataBits.text() text = ''.join(c for c in text if c == "1" or c == "0") self.ui.linEdDataBits.blockSignals(True) self.ui.linEdDataBits.setText(text) self.ui.linEdDataBits.blockSignals(False) self.current_modulator.display_bits = text self.draw_carrier() self.draw_data_bits() self.draw_modulated() if len(text) > 0: if len(text) > 24: display_text = text[0:24] + "..." else: display_text = text self.ui.cbShowDataBitsOnly.setToolTip(text) self.ui.cbShowDataBitsOnly.setText( self.tr("Show Only Data Sequence\n") + "(" + display_text + ")") else: self.ui.cbShowDataBitsOnly.setToolTip("") self.ui.cbShowDataBitsOnly.setText( self.tr("Show Only Data Sequence\n")) self.search_data_sequence() self.restore_bits_action.setEnabled(text != self.original_bits) self.show_full_scene() @pyqtSlot() def on_sample_rate_changed(self): if int(self.ui.spinBoxSampleRate.value()) > 0: self.current_modulator.sample_rate = int( self.ui.spinBoxSampleRate.value()) self.draw_carrier() self.draw_modulated() @pyqtSlot() def on_gauss_bt_changed(self): self.current_modulator.gauss_bt = self.ui.spinBoxGaussBT.value() self.draw_modulated() @pyqtSlot() def on_gauss_filter_width_changed(self): self.current_modulator.gauss_filter_width = self.ui.spinBoxGaussFilterWidth.value( ) self.draw_modulated() @pyqtSlot() def on_bits_per_symbol_changed(self): if self.current_modulator.bits_per_symbol == self.ui.spinBoxBitsPerSymbol.value( ): return self.current_modulator.bits_per_symbol = self.ui.spinBoxBitsPerSymbol.value( ) self.set_default_modulation_parameters() self.draw_modulated() self.show_full_scene() @pyqtSlot() def on_modulation_type_changed(self): write_default_parameters = self.current_modulator.modulation_type != self.__cur_selected_mod_type( ) self.current_modulator.modulation_type = self.__cur_selected_mod_type() self.__set_gauss_ui_visibility( self.__cur_selected_mod_type() == "GFSK") self.ui.labelParameters.setText( self.current_modulator.parameter_type_str) if write_default_parameters: self.set_default_modulation_parameters() else: self.update_modulation_parameters() self.set_bits_per_symbol_enabled_status() self.draw_modulated() self.show_full_scene() @pyqtSlot() def on_orig_signal_zoomed(self): start = self.ui.gVOriginalSignal.view_rect().x() end = start + self.ui.gVOriginalSignal.view_rect().width() self.ui.gVOriginalSignal.centerOn(start + (end - start) / 2, 0) if self.lock_samples_in_view: self.adjust_samples_in_view( self.ui.gVOriginalSignal.view_rect().width()) x = self.ui.gVOriginalSignal.view_rect().x( ) + self.ui.gVOriginalSignal.view_rect().width() / 2 y = 0 self.ui.gVModulated.centerOn(x, y) self.ui.gVCarrier.centerOn(x, y) self.ui.gVData.centerOn(x, y) else: self.mark_samples_in_view() @pyqtSlot(float) def on_carrier_data_modulated_zoomed(self, factor: float): x = self.sender().view_rect().x( ) + self.sender().view_rect().width() / 2 y = 0 for gv in (self.ui.gVCarrier, self.ui.gVData, self.ui.gVModulated): if gv == self.sender(): continue if factor == -1: gv.show_full_scene() else: gv.scale(factor, 1) gv.centerOn(x, y) if self.lock_samples_in_view: self.adjust_samples_in_view( self.ui.gVModulated.view_rect().width()) self.ui.gVOriginalSignal.centerOn(x, y) else: self.mark_samples_in_view() @pyqtSlot() def on_custom_modulation_name_edited(self): self.current_modulator.name = self.ui.comboBoxCustomModulations.currentText( ) @pyqtSlot() def on_custom_modulation_index_changed(self): self.set_ui_for_current_modulator() self.draw_carrier() self.draw_data_bits() self.draw_modulated() @pyqtSlot() def on_btn_next_search_result_clicked(self): cur_search_result = int(self.ui.lCurrentSearchResult.text()) - 1 self.show_search_result(cur_search_result + 1) @pyqtSlot() def on_btn_prev_search_result_clicked(self): cur_search_result = int(self.ui.lCurrentSearchResult.text()) - 1 self.show_search_result(cur_search_result - 1) @pyqtSlot() def on_show_data_bits_only_changed(self, redraw=True): show_data_bits_only = self.ui.cbShowDataBitsOnly.isChecked() if not self.ui.cbShowDataBitsOnly.isEnabled( ) or not show_data_bits_only: self.ui.btnSearchPrev.setEnabled(False) self.ui.btnSearchNext.setEnabled(False) self.ui.lCurrentSearchResult.setText("-") self.ui.lTotalSearchresults.setText("-") else: self.search_data_sequence() if not redraw: return if self.ui.cbShowDataBitsOnly.isEnabled() and not show_data_bits_only: self.draw_original_signal() @pyqtSlot() def on_remove_modulator_clicked(self): index = self.ui.comboBoxCustomModulations.currentIndex() self.ui.comboBoxCustomModulations.removeItem(index) self.modulators.remove(self.modulators[index]) if len(self.modulators) == 1: self.ui.btnRemoveModulation.setDisabled(True) @pyqtSlot() def on_lock_siv_changed(self): self.lock_samples_in_view = self.ui.chkBoxLockSIV.isChecked() if self.lock_samples_in_view: self.adjust_samples_in_view( self.ui.gVModulated.view_rect().width()) @pyqtSlot() def on_restore_bits_action_triggered(self): self.ui.linEdDataBits.setText(self.original_bits) @pyqtSlot() def on_btn_autodetect_clicked(self): signal = self.ui.gVOriginalSignal.scene_manager.signal freq = self.current_modulator.estimate_carrier_frequency( signal, self.protocol) if freq is None or freq == 0: QMessageBox.information( self, self.tr("No results"), self.tr("Unable to detect parameters from current signal")) return self.ui.doubleSpinBoxCarrierFreq.setValue(freq) self.detect_fsk_frequencies() @pyqtSlot(int) def on_modulated_selection_changed(self, new_width: int): self.ui.lModulatedSelectedSamples.setText(str(abs(new_width))) @pyqtSlot(int) def on_original_selection_changed(self, new_width: int): self.ui.lOriginalSignalSamplesSelected.setText(str(abs(new_width))) @pyqtSlot() def on_configure_parameters_action_triggered(self): self.ui.lineEditParameters.editingFinished.emit() dialog = ModulationParametersDialog( self.current_modulator.parameters, self.current_modulator.modulation_type, self) dialog.accepted.connect(self.update_modulation_parameters) dialog.show() @pyqtSlot() def on_line_edit_parameters_editing_finished(self): if not self.ui.lineEditParameters.hasAcceptableInput(): return text = self.ui.lineEditParameters.text() parameters = [] for param in text.split("/"): param = param.upper().replace(",", ".") factor = 1 if param.endswith("G"): factor = 10**9 param = param[:-1] elif param.endswith("M"): factor = 10**6 param = param[:-1] elif param.endswith("K"): factor = 10**3 param = param[:-1] try: parameters.append(factor * float(param)) except ValueError: logger.warning("Could not convert {} to number".format(param)) return self.current_modulator.parameters[:] = array("f", parameters) self.draw_modulated() self.show_full_scene()
class ModulatorDialogController(QDialog): def __init__(self, modulators, parent=None): """ :type modulators: list of Modulator """ super().__init__(parent) self.ui = Ui_DialogModulation() self.ui.setupUi(self) self.setAttribute(Qt.WA_DeleteOnClose) self.lock_samples_in_view = False self.ui.comboBoxCustomModulations.clear() for modulator in modulators: self.ui.comboBoxCustomModulations.addItem(modulator.name) if len(modulators) == 1: self.ui.btnRemoveModulation.setDisabled(True) self.modulators = modulators self.ui.doubleSpinBoxCarrierFreq.setValue( self.current_modulator.carrier_freq_hz) self.ui.doubleSpinBoxCarrierPhase.setValue( self.current_modulator.carrier_phase_deg) self.ui.spinBoxBitLength.setValue( self.current_modulator.samples_per_bit) self.ui.spinBoxSampleRate.setValue(self.current_modulator.sample_rate) self.ui.spinBoxParameter0.setValue( self.current_modulator.param_for_zero) self.ui.spinBoxParameter1.setValue( self.current_modulator.param_for_one) self.ui.cbShowDataBitsOnly.setText( self.tr("Show Only Data Sequence\n")) self.ui.cbShowDataBitsOnly.setEnabled(False) self.protocol = None """:type: ProtocolAnalyzer """ self.search_results = [] self.ui.cbShowDataBitsOnly.setEnabled(False) self.ui.btnSearchNext.setEnabled(False) self.ui.btnSearchPrev.setEnabled(False) self.ui.chkBoxLockSIV.setDisabled(True) self.ui.comboBoxModulationType.setCurrentIndex( self.current_modulator.modulation_type) self.create_connects() self.on_modulation_type_changed() self.original_bits = "" self.ui.btnRestoreBits.setEnabled(False) @property def current_modulator(self): mod = self.modulators[self.ui.comboBoxCustomModulations.currentIndex()] return mod def create_connects(self): self.ui.doubleSpinBoxCarrierFreq.valueChanged.connect( self.on_carrier_freq_changed) self.ui.doubleSpinBoxCarrierPhase.valueChanged.connect( self.on_carrier_phase_changed) self.ui.spinBoxBitLength.valueChanged.connect(self.on_bit_len_changed) self.ui.spinBoxSampleRate.valueChanged.connect( self.on_sample_rate_changed) self.ui.linEdDataBits.textChanged.connect(self.on_data_bits_changed) self.ui.spinBoxParameter0.valueChanged.connect( self.on_modulation_parameter_zero_changed) self.ui.spinBoxParameter1.valueChanged.connect( self.on_modulation_parameter_one_changed) self.ui.comboBoxModulationType.currentIndexChanged.connect( self.on_modulation_type_changed) self.ui.gVOriginalSignal.zoomed.connect(self.on_orig_signal_zoomed) self.ui.cbShowDataBitsOnly.stateChanged.connect( self.on_show_data_bits_only_changed) self.ui.btnSearchNext.clicked.connect( self.on_btn_next_search_result_clicked) self.ui.btnSearchPrev.clicked.connect( self.on_btn_prev_search_result_clicked) self.ui.comboBoxCustomModulations.editTextChanged.connect( self.on_custom_modulation_name_edited) self.ui.comboBoxCustomModulations.currentIndexChanged.connect( self.on_custom_modulation_index_changed) self.ui.btnAddModulation.clicked.connect(self.add_modulator) self.ui.btnRemoveModulation.clicked.connect( self.on_remove_modulator_clicked) self.ui.gVModulated.zoomed.connect( self.on_carrier_data_modulated_zoomed) self.ui.gVCarrier.zoomed.connect(self.on_carrier_data_modulated_zoomed) self.ui.gVData.zoomed.connect(self.on_carrier_data_modulated_zoomed) self.ui.gVModulated.sel_area_width_changed.connect( self.on_modulated_selection_changed) self.ui.gVOriginalSignal.sel_area_width_changed.connect( self.on_original_selection_changed) self.ui.chkBoxLockSIV.stateChanged.connect(self.on_lock_siv_changed) self.ui.btnRestoreBits.clicked.connect( self.on_btn_restore_bits_clicked) self.ui.btnSaveAndClose.clicked.connect(self.close) self.ui.gVOriginalSignal.signal_loaded.connect( self.handle_signal_loaded) self.ui.btnAutoDetect.clicked.connect(self.on_btnautodetect_clicked) def draw_carrier(self): self.ui.gVCarrier.plot_data(self.current_modulator.carrier_data) def draw_data_bits(self): self.ui.gVData.setScene(self.current_modulator.data_scene) self.ui.gVData.update() def draw_modulated(self): self.current_modulator.modulate(pause=0) self.ui.gVModulated.plot_data( self.current_modulator.modulated_samples.imag.astype( numpy.float32)) if self.lock_samples_in_view: siv = self.ui.gVOriginalSignal.view_rect().width() self.adjust_samples_in_view(siv) else: self.mark_samples_in_view() def draw_original_signal(self, start=0, end=-1): scene_creator = self.ui.gVOriginalSignal.scene_creator if scene_creator is None: return if end == -1: end = scene_creator.signal.num_samples self.ui.gVOriginalSignal.horizontalScrollBar().blockSignals(True) y = scene_creator.scene.sceneRect().y() h = scene_creator.scene.sceneRect().height() self.ui.gVOriginalSignal.setSceneRect(start, y, end - start, h) self.ui.gVOriginalSignal.fitInView( self.ui.gVOriginalSignal.sceneRect()) scene_creator.show_scene_section(start, end) self.ui.gVOriginalSignal.update() if self.lock_samples_in_view: self.adjust_samples_in_view( self.ui.gVModulated.view_rect().width()) else: self.mark_samples_in_view() @pyqtSlot() def on_carrier_freq_changed(self): self.current_modulator.carrier_freq_hz = self.ui.doubleSpinBoxCarrierFreq.value( ) self.draw_carrier() self.draw_modulated() @pyqtSlot() def on_carrier_phase_changed(self): self.current_modulator.carrier_phase_deg = self.ui.doubleSpinBoxCarrierPhase.value( ) self.draw_carrier() self.draw_modulated() @pyqtSlot() def on_bit_len_changed(self): self.current_modulator.samples_per_bit = self.ui.spinBoxBitLength.value( ) self.draw_carrier() self.draw_data_bits() self.draw_modulated() @pyqtSlot() def on_data_bits_changed(self): text = self.ui.linEdDataBits.text() text = ''.join(c for c in text if c == "1" or c == "0") self.ui.linEdDataBits.blockSignals(True) self.ui.linEdDataBits.setText(text) self.ui.linEdDataBits.blockSignals(False) self.current_modulator.display_bits = text self.draw_carrier() self.draw_data_bits() self.draw_modulated() if len(text) > 0: self.ui.cbShowDataBitsOnly.setText( self.tr("Show Only Data Sequence\n") + "(" + text + ")") else: self.ui.cbShowDataBitsOnly.setText( self.tr("Show Only Data Sequence\n")) self.search_data_sequence() if text == self.original_bits: self.ui.btnRestoreBits.setDisabled(True) else: self.ui.btnRestoreBits.setEnabled(True) @pyqtSlot() def on_sample_rate_changed(self): if int(self.ui.spinBoxSampleRate.value()) > 0: self.current_modulator.sample_rate = int( self.ui.spinBoxSampleRate.value()) self.draw_carrier() self.draw_modulated() @pyqtSlot() def on_modulation_parameter_zero_changed(self): self.current_modulator.param_for_zero = self.ui.spinBoxParameter0.value( ) self.draw_modulated() @pyqtSlot() def on_modulation_parameter_one_changed(self): self.current_modulator.param_for_one = self.ui.spinBoxParameter1.value( ) self.draw_modulated() @pyqtSlot() def on_modulation_type_changed(self): if self.current_modulator.modulation_type == self.ui.comboBoxModulationType.currentIndex( ): write_standard_parameters = False else: self.current_modulator.modulation_type = self.ui.comboBoxModulationType.currentIndex( ) write_standard_parameters = True if self.ui.comboBoxModulationType.currentIndex() == 0: self.ui.lParameterfor0.setText(self.tr("Amplitude for 0:")) self.ui.lParameterfor1.setText(self.tr("Amplitude for 1:")) self.ui.spinBoxParameter0.auto_suffix = False self.ui.spinBoxParameter1.auto_suffix = False self.ui.spinBoxParameter0.setMaximum(100) self.ui.spinBoxParameter0.setMinimum(0) self.ui.spinBoxParameter0.setDecimals(0) self.ui.spinBoxParameter0.setSuffix("%") self.ui.spinBoxParameter1.setMaximum(100) self.ui.spinBoxParameter1.setMinimum(0) self.ui.spinBoxParameter1.setDecimals(0) self.ui.spinBoxParameter1.setSuffix("%") if write_standard_parameters: self.ui.spinBoxParameter0.setValue(0) self.ui.spinBoxParameter1.setValue(100) else: self.ui.spinBoxParameter0.setValue( self.current_modulator.param_for_zero) self.ui.spinBoxParameter1.setValue( self.current_modulator.param_for_one) elif self.ui.comboBoxModulationType.currentIndex() == 1: self.ui.spinBoxParameter0.auto_suffix = True self.ui.spinBoxParameter1.auto_suffix = True self.ui.lParameterfor0.setText(self.tr("Frequency for 0:")) self.ui.lParameterfor1.setText(self.tr("Frequency for 1:")) self.ui.spinBoxParameter0.setMaximum(1e12) self.ui.spinBoxParameter0.setMinimum(-1e12) self.ui.spinBoxParameter0.setDecimals(4) self.ui.spinBoxParameter0.setSuffix("") self.ui.spinBoxParameter1.setMaximum(1e12) self.ui.spinBoxParameter1.setMinimum(-1e12) self.ui.spinBoxParameter1.setDecimals(4) self.ui.spinBoxParameter1.setSuffix("") if write_standard_parameters: self.autodetect_fsk_freqs() else: self.ui.spinBoxParameter0.setValue( self.current_modulator.param_for_zero) self.ui.spinBoxParameter1.setValue( self.current_modulator.param_for_one) elif self.ui.comboBoxModulationType.currentIndex() == 2: self.ui.spinBoxParameter0.auto_suffix = False self.ui.spinBoxParameter1.auto_suffix = False self.ui.lParameterfor0.setText(self.tr("Phase (degree) for 0:")) self.ui.lParameterfor1.setText(self.tr("Phase (degree) for 1:")) self.ui.spinBoxParameter0.setMaximum(360) self.ui.spinBoxParameter0.setMinimum(-360) self.ui.spinBoxParameter0.setDecimals(0) self.ui.spinBoxParameter0.setSuffix("°") self.ui.spinBoxParameter1.setMaximum(360) self.ui.spinBoxParameter1.setMinimum(-360) self.ui.spinBoxParameter1.setDecimals(0) self.ui.spinBoxParameter1.setSuffix("°") if write_standard_parameters: self.ui.spinBoxParameter0.setValue(90) self.ui.spinBoxParameter1.setValue(270) else: self.ui.spinBoxParameter0.setValue( self.current_modulator.param_for_zero) self.ui.spinBoxParameter1.setValue( self.current_modulator.param_for_one) self.draw_modulated() def resizeEvent(self, event: QResizeEvent): self.update_views() def update_views(self): self.ui.gVCarrier.update() self.ui.gVData.update() self.ui.gVModulated.update() self.ui.gVOriginalSignal.update() @pyqtSlot() def on_orig_signal_zoomed(self): start = self.ui.gVOriginalSignal.view_rect().x() end = start + self.ui.gVOriginalSignal.view_rect().width() self.ui.gVOriginalSignal.centerOn(start + (end - start) / 2, 0) if self.lock_samples_in_view: self.adjust_samples_in_view( self.ui.gVOriginalSignal.view_rect().width()) x = self.ui.gVOriginalSignal.view_rect().x( ) + self.ui.gVOriginalSignal.view_rect().width() / 2 y = 0 self.ui.gVModulated.centerOn(x, y) self.ui.gVCarrier.centerOn(x, y) self.ui.gVData.centerOn(x, y) else: self.mark_samples_in_view() @pyqtSlot(float) def on_carrier_data_modulated_zoomed(self, factor: float): x = self.sender().view_rect().x( ) + self.sender().view_rect().width() / 2 y = 0 for gv in (self.ui.gVCarrier, self.ui.gVData, self.ui.gVModulated): if gv == self.sender(): continue gv.scale(factor, 1) gv.centerOn(x, y) if self.lock_samples_in_view: self.adjust_samples_in_view( self.ui.gVModulated.view_rect().width()) self.ui.gVOriginalSignal.centerOn(x, y) else: self.mark_samples_in_view() def mark_samples_in_view(self): self.ui.lSamplesInViewModulated.setText( str(int(self.ui.gVModulated.view_rect().width()))) if self.ui.gVOriginalSignal.scene_creator is not None: self.ui.lSamplesInViewOrigSignal.setText( str(int(self.ui.gVOriginalSignal.view_rect().width()))) else: self.ui.lSamplesInViewOrigSignal.setText("-") if int(self.ui.gVOriginalSignal.view_rect().width()) != int( self.ui.gVModulated.view_rect().width()): font = self.ui.lSamplesInViewModulated.font() font.setBold(False) self.ui.lSamplesInViewModulated.setFont(font) self.ui.lSamplesInViewOrigSignal.setFont(font) self.ui.lSamplesInViewOrigSignal.setStyleSheet( "QLabel { color : red; }") self.ui.lSamplesInViewModulated.setStyleSheet( "QLabel { color : red; }") else: font = self.ui.lSamplesInViewModulated.font() font.setBold(True) self.ui.lSamplesInViewModulated.setFont(font) self.ui.lSamplesInViewOrigSignal.setFont(font) self.ui.lSamplesInViewOrigSignal.setStyleSheet("") self.ui.lSamplesInViewModulated.setStyleSheet("") @pyqtSlot() def on_custom_modulation_name_edited(self): self.current_modulator.name = self.ui.comboBoxCustomModulations.currentText( ) @pyqtSlot() def on_custom_modulation_index_changed(self): self.draw_carrier() self.draw_data_bits() self.draw_modulated() @pyqtSlot() def on_btn_next_search_result_clicked(self): cur_search_result = int(self.ui.lCurrentSearchResult.text()) - 1 self.show_search_result(cur_search_result + 1) @pyqtSlot() def on_btn_prev_search_result_clicked(self): cur_search_result = int(self.ui.lCurrentSearchResult.text()) - 1 self.show_search_result(cur_search_result - 1) @pyqtSlot() def on_show_data_bits_only_changed(self, redraw=True): show_data_bits_only = self.ui.cbShowDataBitsOnly.isChecked() if not self.ui.cbShowDataBitsOnly.isEnabled( ) or not show_data_bits_only: self.ui.btnSearchPrev.setEnabled(False) self.ui.btnSearchNext.setEnabled(False) self.ui.lCurrentSearchResult.setText("-") self.ui.lTotalSearchresults.setText("-") else: self.search_data_sequence() if not redraw: return if self.ui.cbShowDataBitsOnly.isEnabled() and not show_data_bits_only: self.draw_original_signal() def search_data_sequence(self): if not self.ui.cbShowDataBitsOnly.isEnabled( ) or not self.ui.cbShowDataBitsOnly.isChecked(): return search_seq = self.ui.linEdDataBits.text() if len(search_seq) == 0 or self.protocol is None: return self.search_results[:] = [] proto_bits = self.protocol.plain_bits_str len_seq = len(search_seq) for i, block in enumerate(proto_bits): j = block.find(search_seq) while j != -1: self.search_results.append((i, j, j + len_seq)) j = block.find(search_seq, j + 1) self.ui.lTotalSearchresults.setText(str(len(self.search_results))) self.show_search_result(0) def show_search_result(self, i: int): if len(self.search_results) == 0: self.ui.lCurrentSearchResult.setText("0") self.ui.gVOriginalSignal.scene_creator.clear_path() return block, start_index, end_index = self.search_results[i] start, nsamples = self.protocol.get_samplepos_of_bitseq( block, start_index, block, end_index, False) self.draw_original_signal(start=start, end=start + nsamples) self.ui.lCurrentSearchResult.setText(str(i + 1)) if i == len(self.search_results) - 1: self.ui.btnSearchNext.setEnabled(False) else: self.ui.btnSearchNext.setEnabled(True) if i == 0: self.ui.btnSearchPrev.setEnabled(False) else: self.ui.btnSearchPrev.setEnabled(True) def add_modulator(self): names = [m.name for m in self.modulators] name = "Modulation" number = 1 while name in names: name = "Modulation " + str(number) number += 1 self.modulators.append(Modulator(name)) self.ui.comboBoxCustomModulations.addItem(name) self.ui.comboBoxCustomModulations.setCurrentIndex( len(self.modulators) - 1) self.ui.btnRemoveModulation.setEnabled(True) @pyqtSlot() def on_remove_modulator_clicked(self): index = self.ui.comboBoxCustomModulations.currentIndex() self.ui.comboBoxCustomModulations.removeItem(index) self.modulators.remove(self.modulators[index]) if len(self.modulators) == 1: self.ui.btnRemoveModulation.setDisabled(True) def adjust_samples_in_view(self, target_siv: float): self.ui.gVOriginalSignal.scale( self.ui.gVOriginalSignal.view_rect().width() / target_siv, 1) mod_zoom_factor = self.ui.gVModulated.view_rect().width() / target_siv self.ui.gVModulated.scale(mod_zoom_factor, 1) self.ui.gVCarrier.scale(mod_zoom_factor, 1) self.ui.gVData.scale(mod_zoom_factor, 1) self.mark_samples_in_view() @pyqtSlot() def on_lock_siv_changed(self): self.lock_samples_in_view = self.ui.chkBoxLockSIV.isChecked() if self.lock_samples_in_view: self.adjust_samples_in_view( self.ui.gVModulated.view_rect().width()) @pyqtSlot() def on_btn_restore_bits_clicked(self): self.ui.linEdDataBits.setText(self.original_bits) @pyqtSlot() def on_btnautodetect_clicked(self): proto_bits = self.protocol.plain_bits_str block_index = -1 pos = -1 # Lets find a one for i, block in enumerate(proto_bits): j = block.find("1") if j != -1: block_index = i pos = j break if block_index == -1 or pos == -1: QMessageBox.information( self, self.tr("No results"), self.tr("Unable to detect parameters from current signal")) return start, nsamples = self.protocol.get_samplepos_of_bitseq( block_index, pos, block_index, pos + 1, False) signal = self.ui.gVOriginalSignal.scene_creator.signal freq = signal.estimate_frequency(start, start + nsamples, self.current_modulator.sample_rate) self.ui.doubleSpinBoxCarrierFreq.setValue(freq) self.autodetect_fsk_freqs() def autodetect_fsk_freqs(self): if self.ui.comboBoxModulationType.currentIndex() != 1: return try: zero_freq = self.protocol.estimate_frequency_for_zero( self.current_modulator.sample_rate) one_freq = self.protocol.estimate_frequency_for_one( self.current_modulator.sample_rate) zero_freq = self.__trim_number(zero_freq) one_freq = self.__trim_number(one_freq) zero_freq, one_freq = self.__ensure_multitude(zero_freq, one_freq) self.ui.spinBoxParameter0.setValue(zero_freq) self.ui.spinBoxParameter1.setValue(one_freq) except AttributeError: self.ui.spinBoxParameter0.setValue( self.current_modulator.carrier_freq_hz / 2) self.ui.spinBoxParameter1.setValue( self.current_modulator.carrier_freq_hz) def __trim_number(self, number): if abs(number) >= 1e9: # giga return numpy.round(number / 1e9) * 1e9 elif abs(number) >= 1e6: # mega return numpy.round(number / 1e6) * 1e6 elif abs(number) >= 1e3: # Kilo return numpy.round(number / 1e3) * 1e3 else: return number def __ensure_multitude(self, num1, num2): try: if abs(num1) > abs(num2): num1 = abs(int(num1 / num2)) * num2 else: num2 = abs(int(num2 / num1)) * num1 return num1, num2 except Exception: return num1, num2 def handle_signal_loaded(self, protocol): self.setCursor(Qt.WaitCursor) self.ui.cbShowDataBitsOnly.setEnabled(True) self.ui.chkBoxLockSIV.setEnabled(True) self.ui.btnAutoDetect.setEnabled(True) self.protocol = protocol self.unsetCursor() def keyPressEvent(self, event: QKeyEvent): if event.key() == Qt.Key_Enter or event.key() == Qt.Key_Return: return else: super().keyPressEvent(event) @pyqtSlot(int) def on_modulated_selection_changed(self, new_width: int): self.ui.lModulatedSelectedSamples.setText(str(abs(new_width))) @pyqtSlot(int) def on_original_selection_changed(self, new_width: int): self.ui.lOriginalSignalSamplesSelected.setText(str(abs(new_width)))