import locale import numpy import numpy as np from PyQt5.QtCore import Qt, pyqtSlot from PyQt5.QtGui import QFontMetrics from PyQt5.QtWidgets import QInputDialog, QWidget, QUndoStack, QApplication from urh import settings from urh.controller.CompareFrameController import CompareFrameController from urh.controller.dialogs.ContinuousSendDialog import ContinuousSendDialog from urh.controller.dialogs.FuzzingDialog import FuzzingDialog from urh.controller.dialogs.ModulatorDialog import ModulatorDialog from urh.controller.dialogs.SendDialog import SendDialog from urh.models.GeneratorListModel import GeneratorListModel from urh.models.GeneratorTableModel import GeneratorTableModel from urh.models.GeneratorTreeModel import GeneratorTreeModel from urh.plugins.NetworkSDRInterface.NetworkSDRInterfacePlugin import NetworkSDRInterfacePlugin from urh.plugins.PluginManager import PluginManager from urh.plugins.RfCat.RfCatPlugin import RfCatPlugin from urh.signalprocessing.IQArray import IQArray from urh.signalprocessing.Message import Message from urh.signalprocessing.MessageType import MessageType from urh.signalprocessing.Modulator import Modulator from urh.signalprocessing.ProtocoLabel import ProtocolLabel from urh.signalprocessing.ProtocolAnalyzer import ProtocolAnalyzer from urh.ui.actions.Fuzz import Fuzz from urh.ui.ui_generator import Ui_GeneratorTab from urh.util import FileOperator, util from urh.util.Errors import Errors from urh.util.Formatter import Formatter from urh.util.Logger import logger from urh.util.ProjectManager import ProjectManager class GeneratorTabController(QWidget): def __init__(self, compare_frame_controller: CompareFrameController, project_manager: ProjectManager, parent=None): super().__init__(parent) self.ui = Ui_GeneratorTab() self.ui.setupUi(self) util.set_splitter_stylesheet(self.ui.splitter) self.project_manager = project_manager self.ui.treeProtocols.setHeaderHidden(True) self.tree_model = GeneratorTreeModel(compare_frame_controller) self.tree_model.set_root_item(compare_frame_controller.proto_tree_model.rootItem) self.tree_model.controller = self self.ui.treeProtocols.setModel(self.tree_model) self.table_model = GeneratorTableModel(compare_frame_controller.proto_tree_model.rootItem, compare_frame_controller.decodings) self.table_model.controller = self self.ui.tableMessages.setModel(self.table_model) self.label_list_model = GeneratorListModel(None) self.ui.listViewProtoLabels.setModel(self.label_list_model) self.network_sdr_button_orig_tooltip = self.ui.btnNetworkSDRSend.toolTip() self.set_network_sdr_send_button_visibility() self.set_rfcat_button_visibility() self.network_sdr_plugin = NetworkSDRInterfacePlugin() self.rfcat_plugin = RfCatPlugin() self.init_rfcat_plugin() self.modulation_msg_indices = [] self.refresh_modulators() self.on_selected_modulation_changed() self.set_fuzzing_ui_status() self.ui.prBarGeneration.hide() self.create_connects(compare_frame_controller) self.set_modulation_profile_status() def __get_modulator_of_message(self, message: Message) -> Modulator: if message.modulator_index > len(self.modulators) - 1: message.modulator_index = 0 return self.modulators[message.modulator_index] @property def selected_message_index(self) -> int: min_row, _, _, _ = self.ui.tableMessages.selection_range() return min_row # @property def selected_message(self) -> Message: selected_msg_index = self.selected_message_index if selected_msg_index == -1 or selected_msg_index >= len(self.table_model.protocol.messages): return None return self.table_model.protocol.messages[selected_msg_index] @property def active_groups(self): return self.tree_model.groups @property def modulators(self): return self.project_manager.modulators @property def total_modulated_samples(self) -> int: return sum(int(len(msg.encoded_bits) * self.__get_modulator_of_message(msg).samples_per_symbol + msg.pause) for msg in self.table_model.protocol.messages) @modulators.setter def modulators(self, value): assert type(value) == list self.project_manager.modulators = value def create_connects(self, compare_frame_controller): compare_frame_controller.proto_tree_model.modelReset.connect(self.refresh_tree) compare_frame_controller.participant_changed.connect(self.table_model.refresh_vertical_header) self.ui.btnEditModulation.clicked.connect(self.show_modulation_dialog) self.ui.cBoxModulations.currentIndexChanged.connect(self.on_selected_modulation_changed) self.ui.tableMessages.selectionModel().selectionChanged.connect(self.on_table_selection_changed) self.ui.tableMessages.encodings_updated.connect(self.on_table_selection_changed) self.table_model.undo_stack.indexChanged.connect(self.on_undo_stack_index_changed) self.table_model.protocol.qt_signals.line_duplicated.connect(self.refresh_pause_list) self.table_model.protocol.qt_signals.fuzzing_started.connect(self.on_fuzzing_started) self.table_model.protocol.qt_signals.current_fuzzing_message_changed.connect( self.on_current_fuzzing_message_changed) self.table_model.protocol.qt_signals.fuzzing_finished.connect(self.on_fuzzing_finished) self.table_model.first_protocol_added.connect(self.on_first_protocol_added) self.label_list_model.protolabel_fuzzing_status_changed.connect(self.set_fuzzing_ui_status) self.ui.cbViewType.currentIndexChanged.connect(self.on_view_type_changed) self.ui.btnSend.clicked.connect(self.on_btn_send_clicked) self.ui.btnSave.clicked.connect(self.on_btn_save_clicked) self.ui.btnOpen.clicked.connect(self.on_btn_open_clicked) self.project_manager.project_updated.connect(self.on_project_updated) self.table_model.vertical_header_color_status_changed.connect( self.ui.tableMessages.on_vertical_header_color_status_changed) self.label_list_model.protolabel_removed.connect(self.handle_proto_label_removed) self.ui.lWPauses.item_edit_clicked.connect(self.edit_pause_item) self.ui.lWPauses.edit_all_items_clicked.connect(self.edit_all_pause_items) self.ui.lWPauses.itemSelectionChanged.connect(self.on_lWpauses_selection_changed) self.ui.lWPauses.lost_focus.connect(self.on_lWPauses_lost_focus) self.ui.lWPauses.doubleClicked.connect(self.on_lWPauses_double_clicked) self.ui.btnGenerate.clicked.connect(self.generate_file) self.label_list_model.protolabel_fuzzing_status_changed.connect(self.handle_plabel_fuzzing_state_changed) self.ui.btnFuzz.clicked.connect(self.on_btn_fuzzing_clicked) self.ui.tableMessages.create_label_triggered.connect(self.create_fuzzing_label) self.ui.tableMessages.edit_label_triggered.connect(self.show_fuzzing_dialog) self.ui.listViewProtoLabels.selection_changed.connect(self.handle_label_selection_changed) self.ui.listViewProtoLabels.edit_on_item_triggered.connect(self.show_fuzzing_dialog) self.ui.btnNetworkSDRSend.clicked.connect(self.on_btn_network_sdr_clicked) self.ui.btnRfCatSend.clicked.connect(self.on_btn_rfcat_clicked) self.network_sdr_plugin.sending_status_changed.connect(self.on_network_sdr_sending_status_changed) self.network_sdr_plugin.sending_stop_requested.connect(self.on_network_sdr_sending_stop_requested) self.network_sdr_plugin.current_send_message_changed.connect(self.on_send_message_changed) @pyqtSlot() def refresh_tree(self): self.tree_model.beginResetModel() self.tree_model.endResetModel() self.ui.treeProtocols.expandAll() @pyqtSlot() def refresh_table(self): self.table_model.update() self.ui.tableMessages.resize_columns() is_data_there = self.table_model.display_data is not None and len(self.table_model.display_data) > 0 self.ui.btnSend.setEnabled(is_data_there) self.ui.btnGenerate.setEnabled(is_data_there) @pyqtSlot() def refresh_label_list(self): self.label_list_model.message = self.selected_message self.label_list_model.update() @property def generator_undo_stack(self) -> QUndoStack: return self.table_model.undo_stack @pyqtSlot() def on_selected_modulation_changed(self): cur_ind = self.ui.cBoxModulations.currentIndex() min_row, max_row, _, _ = self.ui.tableMessages.selection_range() if min_row > -1: # set modulation for selected messages for row in range(min_row, max_row + 1): try: self.table_model.protocol.messages[row].modulator_index = cur_ind except IndexError: continue self.show_modulation_info() def refresh_modulators(self): current_index = 0 if type(self.sender()) == ModulatorDialog: current_index = self.sender().ui.comboBoxCustomModulations.currentIndex() self.ui.cBoxModulations.clear() for modulator in self.modulators: self.ui.cBoxModulations.addItem(modulator.name) self.ui.cBoxModulations.setCurrentIndex(current_index) def bootstrap_modulator(self, protocol: ProtocolAnalyzer): """ Set initial parameters for default modulator if it was not edited by user previously :return: """ if len(self.modulators) != 1 or len(self.table_model.protocol.messages) == 0: return modulator = self.modulators[0] modulator.samples_per_symbol = protocol.messages[0].samples_per_symbol modulator.bits_per_symbol = protocol.messages[0].bits_per_symbol if protocol.signal: modulator.sample_rate = protocol.signal.sample_rate modulator.modulation_type = protocol.signal.modulation_type auto_freq = modulator.estimate_carrier_frequency(protocol.signal, protocol) if auto_freq is not None and auto_freq != 0: modulator.carrier_freq_hz = auto_freq modulator.parameters = modulator.get_default_parameters() self.show_modulation_info() def show_modulation_info(self): cur_ind = self.ui.cBoxModulations.currentIndex() mod = self.modulators[cur_ind] self.ui.lCarrierFreqValue.setText(mod.carrier_frequency_str) self.ui.lCarrierPhaseValue.setText(mod.carrier_phase_str) self.ui.lBitLenValue.setText(mod.samples_per_symbol_str) self.ui.lSampleRateValue.setText(mod.sample_rate_str) mod_type = mod.modulation_type self.ui.lModTypeValue.setText(mod_type) self.ui.lParamCaption.setText(mod.parameter_type_str) self.ui.labelParameterValues.setText(mod.parameters_string) self.ui.labelBitsPerSymbol.setText(str(mod.bits_per_symbol)) def prepare_modulation_dialog(self) -> (ModulatorDialog, Message): preselected_index = self.ui.cBoxModulations.currentIndex() min_row, max_row, start, end = self.ui.tableMessages.selection_range() if min_row > -1: try: selected_message = self.table_model.protocol.messages[min_row] preselected_index = selected_message.modulator_index except IndexError: selected_message = Message([1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0], 0, [], MessageType("empty")) else: selected_message = Message([1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0], 0, [], MessageType("empty")) if len(self.table_model.protocol.messages) > 0: selected_message.samples_per_symbol = self.table_model.protocol.messages[0].samples_per_symbol for m in self.modulators: m.default_sample_rate = self.project_manager.device_conf["sample_rate"] modulator_dialog = ModulatorDialog(self.modulators, tree_model=self.tree_model, parent=self.parent()) modulator_dialog.ui.comboBoxCustomModulations.setCurrentIndex(preselected_index) modulator_dialog.finished.connect(self.refresh_modulators) modulator_dialog.finished.connect(self.refresh_pause_list) return modulator_dialog, selected_message def set_modulation_profile_status(self): visible = settings.read("multiple_modulations", False, bool) self.ui.cBoxModulations.setVisible(visible) def init_rfcat_plugin(self): self.set_rfcat_button_visibility() self.rfcat_plugin = RfCatPlugin() self.rfcat_plugin.current_send_message_changed.connect(self.on_send_message_changed) self.ui.btnRfCatSend.setEnabled(self.rfcat_plugin.rfcat_is_found) @pyqtSlot() def on_undo_stack_index_changed(self): self.refresh_table() self.refresh_pause_list() self.refresh_label_list() self.refresh_estimated_time() self.set_fuzzing_ui_status() @pyqtSlot() def show_modulation_dialog(self): modulator_dialog, message = self.prepare_modulation_dialog() modulator_dialog.showMaximized() modulator_dialog.initialize(message.encoded_bits_str[0:16]) self.project_manager.modulation_was_edited = True @pyqtSlot() def on_table_selection_changed(self): min_row, max_row, start, end = self.ui.tableMessages.selection_range() if min_row == -1: self.ui.lEncodingValue.setText("-") # self.ui.lEncodingValue.setToolTip("") self.label_list_model.message = None return container = self.table_model.protocol message = container.messages[min_row] self.label_list_model.message = message decoder_name = message.decoder.name metrics = QFontMetrics(self.ui.lEncodingValue.font()) elidedName = metrics.elidedText(decoder_name, Qt.ElideRight, self.ui.lEncodingValue.width()) self.ui.lEncodingValue.setText(elidedName) self.ui.lEncodingValue.setToolTip(decoder_name) self.ui.cBoxModulations.blockSignals(True) self.ui.cBoxModulations.setCurrentIndex(message.modulator_index) self.show_modulation_info() self.ui.cBoxModulations.blockSignals(False) @pyqtSlot(int) def edit_pause_item(self, index: int): message = self.table_model.protocol.messages[index] cur_len = message.pause new_len, ok = QInputDialog.getInt(self, self.tr("Enter new Pause Length"), self.tr("Pause Length:"), cur_len, 0) if ok: message.pause = new_len self.refresh_pause_list() @pyqtSlot() def edit_all_pause_items(self): message = self.table_model.protocol.messages[0] cur_len = message.pause new_len, ok = QInputDialog.getInt(self, self.tr("Enter new Pause Length"), self.tr("Pause Length:"), cur_len, 0) if ok: for message in self.table_model.protocol.messages: message.pause = new_len self.refresh_pause_list() @pyqtSlot() def on_lWPauses_double_clicked(self): sel_indexes = [index.row() for index in self.ui.lWPauses.selectedIndexes()] if len(sel_indexes) > 0: self.edit_pause_item(sel_indexes[0]) @pyqtSlot() def refresh_pause_list(self): self.ui.lWPauses.clear() fmt_str = "Pause ({1:d}-{2:d}) <{0:d} samples ({3})>" for i, pause in enumerate(self.table_model.protocol.pauses): sr = self.__get_modulator_of_message(self.table_model.protocol.messages[i]).sample_rate item = fmt_str.format(pause, i + 1, i + 2, Formatter.science_time(pause / sr)) self.ui.lWPauses.addItem(item) self.refresh_estimated_time() @pyqtSlot() def on_lWpauses_selection_changed(self): rows = [index.row() for index in self.ui.lWPauses.selectedIndexes()] if len(rows) == 0: return self.ui.tableMessages.show_pause_active = True self.ui.tableMessages.pause_row = rows[0] self.ui.tableMessages.viewport().update() self.ui.tableMessages.scrollTo(self.table_model.index(rows[0], 0)) @pyqtSlot() def on_lWPauses_lost_focus(self): self.ui.tableMessages.show_pause_active = False self.ui.tableMessages.viewport().update() @pyqtSlot() def generate_file(self): try: total_samples = self.total_modulated_samples buffer = self.prepare_modulation_buffer(total_samples, show_error=False) if buffer is None: Errors.generic_error(self.tr("File too big"), self.tr("This file would get too big to save.")) self.unsetCursor() return modulated_samples = self.modulate_data(buffer) try: sample_rate = self.modulators[0].sample_rate except Exception as e: logger.exception(e) sample_rate = 1e6 FileOperator.ask_signal_file_name_and_save("generated", modulated_samples, sample_rate=sample_rate, parent=self) except Exception as e: Errors.exception(e) self.unsetCursor() def prepare_modulation_buffer(self, total_samples: int, show_error=True) -> IQArray: dtype = Modulator.get_dtype() n = 2 if dtype == np.int8 else 4 if dtype == np.int16 else 8 memory_size_for_buffer = total_samples * n logger.debug("Allocating {0:.2f}MB for modulated samples".format(memory_size_for_buffer / (1024 ** 2))) try: # allocate it three times as we need the same amount for the sending process IQArray(None, dtype=dtype, n=3*total_samples) except MemoryError: # will go into continuous mode in this case if show_error: Errors.not_enough_ram_for_sending_precache(3*memory_size_for_buffer) return None return IQArray(None, dtype=dtype, n=total_samples) def modulate_data(self, buffer: IQArray) -> IQArray: """ :param buffer: Buffer in which the modulated data shall be written, initialized with zeros :return: """ self.ui.prBarGeneration.show() self.ui.prBarGeneration.setValue(0) self.ui.prBarGeneration.setMaximum(self.table_model.row_count) self.modulation_msg_indices.clear() pos = 0 for i in range(0, self.table_model.row_count): message = self.table_model.protocol.messages[i] modulator = self.__get_modulator_of_message(message) # We do not need to modulate the pause extra, as result is already initialized with zeros modulated = modulator.modulate(start=0, data=message.encoded_bits, pause=0) buffer[pos:pos + len(modulated)] = modulated pos += len(modulated) + message.pause self.modulation_msg_indices.append(pos) self.ui.prBarGeneration.setValue(i + 1) QApplication.instance().processEvents() self.ui.prBarGeneration.hide() return buffer @pyqtSlot(int) def show_fuzzing_dialog(self, label_index: int): view = self.ui.cbViewType.currentIndex() if self.label_list_model.message is not None: msg_index = self.table_model.protocol.messages.index(self.label_list_model.message) fdc = FuzzingDialog(protocol=self.table_model.protocol, label_index=label_index, msg_index=msg_index, proto_view=view, parent=self) fdc.show() fdc.finished.connect(self.on_fuzzing_dialog_finished) @pyqtSlot() def on_fuzzing_dialog_finished(self): self.refresh_label_list() self.refresh_table() self.set_fuzzing_ui_status() self.ui.tabWidget.setCurrentIndex(2) @pyqtSlot() def handle_plabel_fuzzing_state_changed(self): self.refresh_table() self.label_list_model.update() @pyqtSlot(ProtocolLabel) def handle_proto_label_removed(self, plabel: ProtocolLabel): self.refresh_label_list() self.refresh_table() self.set_fuzzing_ui_status() @pyqtSlot() def on_btn_fuzzing_clicked(self): fuz_mode = "Successive" if self.ui.rbConcurrent.isChecked(): fuz_mode = "Concurrent" elif self.ui.rBExhaustive.isChecked(): fuz_mode = "Exhaustive" self.setCursor(Qt.WaitCursor) fuzz_action = Fuzz(self.table_model.protocol, fuz_mode) self.table_model.undo_stack.push(fuzz_action) for row in fuzz_action.added_message_indices: self.table_model.update_checksums_for_row(row) self.unsetCursor() self.ui.tableMessages.setFocus() @pyqtSlot() def set_fuzzing_ui_status(self): btn_was_enabled = self.ui.btnFuzz.isEnabled() fuzz_active = any(lbl.active_fuzzing for msg in self.table_model.protocol.messages for lbl in msg.message_type) self.ui.btnFuzz.setEnabled(fuzz_active) if self.ui.btnFuzz.isEnabled() and not btn_was_enabled: font = self.ui.btnFuzz.font() font.setBold(True) self.ui.btnFuzz.setFont(font) else: font = self.ui.btnFuzz.font() font.setBold(False) self.ui.btnFuzz.setFont(font) self.ui.btnFuzz.setStyleSheet("") has_same_message = self.table_model.protocol.multiple_fuzz_labels_per_message self.ui.rBSuccessive.setEnabled(has_same_message) self.ui.rBExhaustive.setEnabled(has_same_message) self.ui.rbConcurrent.setEnabled(has_same_message) def refresh_existing_encodings(self, encodings_from_file): """ Refresh existing encodings for messages, when encoding was changed by user in dialog :return: """ update = False for msg in self.table_model.protocol.messages: i = next((i for i, d in enumerate(encodings_from_file) if d.name == msg.decoder.name), 0) if msg.decoder != encodings_from_file[i]: update = True msg.decoder = encodings_from_file[i] msg.clear_decoded_bits() msg.clear_encoded_bits() if update: self.refresh_table() self.refresh_estimated_time() @pyqtSlot() def refresh_estimated_time(self): c = self.table_model.protocol if c.num_messages == 0: self.ui.lEstimatedTime.setText("Estimated Time: ") return avg_msg_len = numpy.mean([len(msg.encoded_bits) for msg in c.messages]) avg_samples_per_symbol = numpy.mean([m.samples_per_symbol for m in self.modulators]) avg_sample_rate = numpy.mean([m.sample_rate for m in self.modulators]) pause_samples = sum(c.pauses) nsamples = c.num_messages * avg_msg_len * avg_samples_per_symbol + pause_samples self.ui.lEstimatedTime.setText( locale.format_string("Estimated Time: %.04f seconds", nsamples / avg_sample_rate)) @pyqtSlot(int, int, int) def create_fuzzing_label(self, msg_index: int, start: int, end: int): con = self.table_model.protocol start, end = con.convert_range(start, end - 1, self.ui.cbViewType.currentIndex(), 0, False, msg_index) lbl = con.create_fuzzing_label(start, end, msg_index) self.show_fuzzing_dialog(con.protocol_labels.index(lbl)) @pyqtSlot() def handle_label_selection_changed(self): rows = [index.row() for index in self.ui.listViewProtoLabels.selectedIndexes()] if len(rows) == 0: return maxrow = numpy.max(rows) try: label = self.table_model.protocol.protocol_labels[maxrow] except IndexError: return if label.show and self.selected_message: start, end = self.selected_message.get_label_range(lbl=label, view=self.table_model.proto_view, decode=False) indx = self.table_model.index(0, int((start + end) / 2)) self.ui.tableMessages.scrollTo(indx) @pyqtSlot() def on_view_type_changed(self): self.setCursor(Qt.WaitCursor) self.table_model.proto_view = self.ui.cbViewType.currentIndex() self.ui.tableMessages.resize_columns() self.unsetCursor() @pyqtSlot() def on_btn_send_clicked(self): try: total_samples = self.total_modulated_samples buffer = self.prepare_modulation_buffer(total_samples) if buffer is not None: modulated_data = self.modulate_data(buffer) else: # Enter continuous mode modulated_data = None try: if modulated_data is not None: try: dialog = SendDialog(self.project_manager, modulated_data=modulated_data, modulation_msg_indices=self.modulation_msg_indices, parent=self) except MemoryError: # Not enough memory for device buffer so we need to create a continuous send dialog del modulated_data Errors.not_enough_ram_for_sending_precache(None) dialog = ContinuousSendDialog(self.project_manager, self.table_model.protocol.messages, self.modulators, total_samples, parent=self) else: dialog = ContinuousSendDialog(self.project_manager, self.table_model.protocol.messages, self.modulators, total_samples, parent=self) except OSError as e: logger.exception(e) return if dialog.has_empty_device_list: Errors.no_device() dialog.close() return dialog.device_parameters_changed.connect(self.project_manager.set_device_parameters) dialog.show() dialog.graphics_view.show_full_scene(reinitialize=True) except Exception as e: Errors.exception(e) self.unsetCursor() @pyqtSlot() def on_btn_save_clicked(self): filename = FileOperator.ask_save_file_name("profile.fuzz.xml", caption="Save fuzzing profile") if filename: self.table_model.protocol.to_xml_file(filename, decoders=self.project_manager.decodings, participants=self.project_manager.participants, modulators=self.modulators) @pyqtSlot() def on_btn_open_clicked(self): dialog = FileOperator.get_open_dialog(directory_mode=False, parent=self, name_filter="fuzz") if dialog.exec_(): for filename in dialog.selectedFiles(): self.load_from_file(filename) def load_from_file(self, filename: str): try: self.modulators = ProjectManager.read_modulators_from_file(filename) self.table_model.protocol.from_xml_file(filename) self.refresh_pause_list() self.refresh_estimated_time() self.refresh_modulators() self.show_modulation_info() self.refresh_table() self.set_fuzzing_ui_status() except: logger.error("You done something wrong to the xml fuzzing profile.") @pyqtSlot() def on_project_updated(self): self.table_model.refresh_vertical_header() def set_network_sdr_send_button_visibility(self): is_plugin_enabled = PluginManager().is_plugin_enabled("NetworkSDRInterface") self.ui.btnNetworkSDRSend.setVisible(is_plugin_enabled) def set_rfcat_button_visibility(self): is_plugin_enabled = PluginManager().is_plugin_enabled("RfCat") self.ui.btnRfCatSend.setVisible(is_plugin_enabled) @pyqtSlot() def on_btn_network_sdr_clicked(self): if not self.network_sdr_plugin.is_sending: messages = self.table_model.protocol.messages sample_rates = [self.__get_modulator_of_message(msg).sample_rate for msg in messages] self.network_sdr_plugin.start_message_sending_thread(messages, sample_rates) else: self.network_sdr_plugin.stop_sending_thread() @pyqtSlot(bool) def on_network_sdr_sending_status_changed(self, is_sending: bool): self.ui.btnNetworkSDRSend.setChecked(is_sending) self.ui.btnNetworkSDRSend.setEnabled(True) self.ui.btnNetworkSDRSend.setToolTip( "Sending in progress" if is_sending else self.network_sdr_button_orig_tooltip) if not is_sending: self.ui.tableMessages.clearSelection() @pyqtSlot() def on_network_sdr_sending_stop_requested(self): self.ui.btnNetworkSDRSend.setToolTip("Stopping sending") self.ui.btnNetworkSDRSend.setEnabled(False) @pyqtSlot(int) def on_send_message_changed(self, message_index: int): self.ui.tableMessages.selectRow(message_index) @pyqtSlot() def on_btn_rfcat_clicked(self): if not self.rfcat_plugin.is_sending: messages = self.table_model.protocol.messages sample_rates = [self.__get_modulator_of_message(msg).sample_rate for msg in messages] self.rfcat_plugin.start_message_sending_thread(messages, sample_rates, self.modulators, self.project_manager) else: self.rfcat_plugin.stop_sending_thread() @pyqtSlot(int) def on_fuzzing_started(self, num_values: int): self.ui.stackedWidgetFuzzing.setCurrentWidget(self.ui.pageFuzzingProgressBar) self.ui.progressBarFuzzing.setMaximum(num_values) self.ui.progressBarFuzzing.setValue(0) QApplication.instance().processEvents() @pyqtSlot() def on_fuzzing_finished(self): self.ui.stackedWidgetFuzzing.setCurrentWidget(self.ui.pageFuzzingUI) # Calculate Checksums for Fuzzed Messages self.setCursor(Qt.WaitCursor) self.unsetCursor() @pyqtSlot(int) def on_current_fuzzing_message_changed(self, current_message: int): self.ui.progressBarFuzzing.setValue(current_message) QApplication.instance().processEvents() @pyqtSlot(ProtocolAnalyzer) def on_first_protocol_added(self, protocol: ProtocolAnalyzer): if not self.project_manager.modulation_was_edited: self.bootstrap_modulator(protocol)