Esempio n. 1
0
    def __init__(self, compare_frame_controller: CompareFrameController, project_manager: ProjectManager, parent=None):
        super().__init__(parent)
        self.ui = Ui_GeneratorTab()
        self.ui.setupUi(self)

        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,
                                               [Modulator("Modulation")], 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.refresh_modulators()
        self.on_selected_modulation_changed()
        self.set_fuzzing_ui_status()
        self.ui.prBarGeneration.hide()
        self.create_connects(compare_frame_controller)
    def __init__(self, compare_frame_controller: CompareFrameController, project_manager: ProjectManager, parent=None):
        super().__init__(parent)
        self.ui = Ui_GeneratorTab()
        self.ui.setupUi(self)

        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.has_default_modulation = True

        self.table_model = GeneratorTableModel(compare_frame_controller.proto_tree_model.rootItem,
                                               [Modulator("Modulation")], 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.refresh_modulators()
        self.on_selected_modulation_changed()
        self.set_fuzzing_ui_status()
        self.project_manager = project_manager
        self.ui.prBarGeneration.hide()
        self.create_connects(compare_frame_controller)
Esempio n. 3
0
    def __init__(self,
                 compare_frame_controller: CompareFrameController,
                 project_manager: ProjectManager,
                 encoders,
                 parent=None):
        """
        :type encoders: list of encoding
        :return:
        """
        super().__init__(parent)
        self.ui = Ui_GeneratorTab()
        self.ui.setupUi(self)

        self.encoders = encoders
        self.modulated_scene_is_locked = False

        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.modulators = [Modulator("Modulation")]
        """:type: list of Modulator """

        self.has_default_modulation = True

        self.table_model = GeneratorTableModel(
            compare_frame_controller.proto_tree_model.rootItem,
            self.modulators, self.encoders)
        """:type: GeneratorTableModel """
        self.table_model.controller = self
        self.ui.tableBlocks.setModel(self.table_model)

        self.label_list_model = GeneratorListModel(self.table_model.protocol)
        self.ui.listViewProtoLabels.setModel(self.label_list_model)

        self.refresh_modulators()
        self.on_selected_modulation_changed()
        self.set_fuzzing_ui_status()
        self.project_manager = project_manager
        self.ui.prBarGeneration.hide()
        self.create_connects(compare_frame_controller)
Esempio n. 4
0
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)

        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.has_default_modulation = True

        self.table_model = GeneratorTableModel(
            compare_frame_controller.proto_tree_model.rootItem,
            [Modulator("Modulation")], 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.refresh_modulators()
        self.on_selected_modulation_changed()
        self.set_fuzzing_ui_status()
        self.project_manager = project_manager
        self.ui.prBarGeneration.hide()
        self.create_connects(compare_frame_controller)

    @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.table_model.protocol.modulators

    @property
    def total_modulated_samples(self) -> int:
        return sum(
            int(
                len(msg.encoded_bits) *
                self.modulators[msg.modulator_indx].samples_per_bit +
                msg.pause) for msg in self.table_model.protocol.messages)

    @modulators.setter
    def modulators(self, value):
        assert type(value) == list
        self.table_model.protocol.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.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.project_manager.project_updated.connect(self.on_project_updated)

        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.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_fuzzing_label_clicked.connect(
            self.create_fuzzing_label)
        self.ui.tableMessages.edit_fuzzing_label_clicked.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:
            # Modulation für Selektierte Blöcke setzen
            for row in range(min_row, max_row + 1):
                try:
                    self.table_model.protocol.messages[
                        row].modulator_indx = cur_ind
                except IndexError:
                    continue

        self.show_modulation_info()

    def refresh_modulators(self):
        current_index = 0
        if type(self.sender()) == ModulatorDialogController:
            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 show_modulation_info(self):
        show = not self.has_default_modulation or self.modulators[
            0] != Modulator("Modulation")

        if not show:
            self.ui.btnEditModulation.setStyleSheet("background: orange")
            font = QFont()
            font.setBold(True)
            self.ui.btnEditModulation.setFont(font)
        else:
            self.ui.btnEditModulation.setStyleSheet("")
            self.ui.btnEditModulation.setFont(QFont())

        cur_ind = self.ui.cBoxModulations.currentIndex()
        cur_mod = self.modulators[cur_ind]
        self.ui.lCarrierFreqValue.setText(cur_mod.carrier_frequency_str)
        self.ui.lCarrierPhaseValue.setText(cur_mod.carrier_phase_str)
        self.ui.lBitLenValue.setText(cur_mod.bit_len_str)
        self.ui.lSampleRateValue.setText(cur_mod.sample_rate_str)
        mod_type = cur_mod.modulation_type_str
        self.ui.lModTypeValue.setText(mod_type)
        if mod_type == "ASK":
            prefix = "Amplitude"
        elif mod_type == "PSK":
            prefix = "Phase"
        elif mod_type in ("FSK", "GFSK"):
            prefix = "Frequency"
        else:
            prefix = "Unknown Modulation Type (This should not happen...)"

        self.ui.lParamForZero.setText(prefix + " for 0:")
        self.ui.lParamForZeroValue.setText(cur_mod.param_for_zero_str)
        self.ui.lParamForOne.setText(prefix + " for 1:")
        self.ui.lParamForOneValue.setText(cur_mod.param_for_one_str)

    def prepare_modulation_dialog(
            self) -> (ModulatorDialogController, 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_indx
            except IndexError:
                selected_message = Message([True, False, True, False], 0, [],
                                           MessageType("empty"))
        else:
            selected_message = Message([True, False, True, False], 0, [],
                                       MessageType("empty"))
            if len(self.table_model.protocol.messages) > 0:
                selected_message.bit_len = self.table_model.protocol.messages[
                    0].bit_len

        for m in self.modulators:
            m.default_sample_rate = self.project_manager.device_conf[
                "sample_rate"]

        modulator_dialog = ModulatorDialogController(self.modulators,
                                                     parent=self.parent())
        modulator_dialog.ui.treeViewSignals.setModel(self.tree_model)
        modulator_dialog.ui.treeViewSignals.expandAll()
        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 initialize_modulation_dialog(self, bits: str,
                                     dialog: ModulatorDialogController):
        dialog.on_modulation_type_changed(
        )  # for drawing modulated signal initially
        dialog.original_bits = bits
        dialog.ui.linEdDataBits.setText(bits)
        dialog.ui.gVOriginalSignal.signal_tree_root = self.tree_model.rootItem
        dialog.draw_original_signal()
        dialog.ui.gVModulated.show_full_scene(reinitialize=True)
        dialog.ui.gVData.show_full_scene(reinitialize=True)
        dialog.ui.gVData.auto_fit_view()
        dialog.ui.gVCarrier.show_full_scene(reinitialize=True)
        dialog.ui.gVCarrier.auto_fit_view()

    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()

        self.initialize_modulation_dialog(message.encoded_bits_str[0:10],
                                          modulator_dialog)
        self.has_default_modulation = False

    @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_indx)
        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 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.modulators[self.table_model.protocol.messages[i].
                                 modulator_indx].sample_rate
            item = fmt_str.format(pause, i + 1, i + 2,
                                  Formatter.science_time(pause / sr))
            self.ui.lWPauses.addItem(item)

    @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)
            FileOperator.save_data_dialog("", modulated_samples, parent=self)
        except Exception as e:
            Errors.generic_error(self.tr("Failed to generate data"), str(e),
                                 traceback.format_exc())
            self.unsetCursor()

    def prepare_modulation_buffer(self,
                                  total_samples: int,
                                  show_error=True) -> np.ndarray:
        memory_size_for_buffer = total_samples * 8
        logger.debug("Allocating {0:.2f}MB for modulated samples".format(
            memory_size_for_buffer / (1024**2)))
        try:
            return np.zeros(total_samples, dtype=np.complex64)
        except MemoryError:
            if show_error:
                Errors.not_enough_ram_for_sending_precache(
                    memory_size_for_buffer)
            return None

    def modulate_data(self, buffer: np.ndarray) -> np.ndarray:
        """
        
        :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)

        pos = 0
        for i in range(0, self.table_model.row_count):
            message = self.table_model.protocol.messages[i]
            modulator = self.modulators[message.modulator_indx]
            # We do not need to modulate the pause extra, as result is already initialized with zeros
            modulator.modulate(start=pos, data=message.encoded_bits, pause=0)
            buffer[pos:pos + len(modulator.modulated_samples
                                 )] = modulator.modulated_samples
            pos += len(modulator.modulated_samples) + message.pause
            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 = FuzzingDialogController(protocol=self.table_model.protocol,
                                          label_index=label_index,
                                          msg_index=msg_index,
                                          proto_view=view,
                                          parent=self)
            fdc.show()
            fdc.finished.connect(self.refresh_label_list)
            fdc.finished.connect(self.refresh_table)
            fdc.finished.connect(self.set_fuzzing_ui_status)

    @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)
        self.unsetCursor()

    @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_bit_len = numpy.mean([m.samples_per_bit 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_bit_len + 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 = SendDialogController(
                            self.project_manager,
                            modulated_data=modulated_data,
                            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 = ContinuousSendDialogController(
                            self.project_manager,
                            self.table_model.protocol.messages,
                            self.modulators,
                            total_samples,
                            parent=self)
                else:
                    dialog = ContinuousSendDialogController(
                        self.project_manager,
                        self.table_model.protocol.messages,
                        self.modulators,
                        total_samples,
                        parent=self)
            except OSError as e:
                logger.error(repr(e))
                return
            if dialog.has_empty_device_list:
                Errors.no_device()
                dialog.close()
                return

            dialog.recording_parameters.connect(
                self.project_manager.set_recording_parameters)
            dialog.show()
            dialog.graphics_view.show_full_scene(reinitialize=True)
        except Exception as e:
            Errors.generic_error(self.tr("Failed to generate data"), str(e),
                                 traceback.format_exc())
            self.unsetCursor()

    @pyqtSlot()
    def on_btn_save_clicked(self):
        filename = FileOperator.get_save_file_name("profile.fuzz",
                                                   caption="Save fuzz profile")
        if filename:
            self.table_model.protocol.to_xml_file(filename)

    def load_from_file(self, filename: str):
        try:
            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.participants = self.project_manager.participants
        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.modulators[msg.modulator_indx].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.modulators[msg.modulator_indx].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)

    @pyqtSlot(int)
    def on_current_fuzzing_message_changed(self, current_message: int):
        self.ui.progressBarFuzzing.setValue(current_message)
        QApplication.instance().processEvents()
Esempio n. 5
0
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_bit + 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_bit = protocol.messages[0].bit_len

        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

        self.show_modulation_info()

    def show_modulation_info(self):
        cur_ind = self.ui.cBoxModulations.currentIndex()
        cur_mod = self.modulators[cur_ind]
        self.ui.lCarrierFreqValue.setText(cur_mod.carrier_frequency_str)
        self.ui.lCarrierPhaseValue.setText(cur_mod.carrier_phase_str)
        self.ui.lBitLenValue.setText(cur_mod.bit_len_str)
        self.ui.lSampleRateValue.setText(cur_mod.sample_rate_str)
        mod_type = cur_mod.modulation_type_str
        self.ui.lModTypeValue.setText(mod_type)
        if mod_type == "ASK":
            prefix = "Amplitude"
        elif mod_type == "PSK":
            prefix = "Phase"
        elif mod_type in ("FSK", "GFSK"):
            prefix = "Frequency"
        else:
            prefix = "Unknown Modulation Type (This should not happen...)"

        self.ui.lParamForZero.setText(prefix + " for 0:")
        self.ui.lParamForZeroValue.setText(cur_mod.param_for_zero_str)
        self.ui.lParamForOne.setText(prefix + " for 1:")
        self.ui.lParamForOneValue.setText(cur_mod.param_for_one_str)

    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], 0, [], MessageType("empty"))
        else:
            selected_message = Message([1, 0, 1, 0, 1, 0, 1, 0], 0, [], MessageType("empty"))
            if len(self.table_model.protocol.messages) > 0:
                selected_message.bit_len = self.table_model.protocol.messages[0].bit_len

        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 = constants.SETTINGS.value("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:10])
        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.save_data_dialog("generated.complex", modulated_samples, sample_rate=sample_rate, parent=self)
        except Exception as e:
            Errors.generic_error(self.tr("Failed to generate data"), str(e), traceback.format_exc())
            self.unsetCursor()

    def prepare_modulation_buffer(self, total_samples: int, show_error=True) -> np.ndarray:
        memory_size_for_buffer = total_samples * 8
        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
            np.zeros(3*total_samples, dtype=np.complex64)
        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 np.zeros(total_samples, dtype=np.complex64)

    def modulate_data(self, buffer: np.ndarray) -> np.ndarray:
        """
        
        :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.refresh_label_list)
            fdc.finished.connect(self.refresh_table)
            fdc.finished.connect(self.set_fuzzing_ui_status)

    @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_bit_len = numpy.mean([m.samples_per_bit 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_bit_len + 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.generic_error(self.tr("Failed to generate data"), str(e), traceback.format_exc())
            self.unsetCursor()

    @pyqtSlot()
    def on_btn_save_clicked(self):
        filename = FileOperator.get_save_file_name("profile.fuzz.xml", caption="Save fuzz 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)
Esempio n. 6
0
class GeneratorTabController(QWidget):
    def __init__(self,
                 compare_frame_controller: CompareFrameController,
                 project_manager: ProjectManager,
                 encoders,
                 parent=None):
        """
        :type encoders: list of encoding
        :return:
        """
        super().__init__(parent)
        self.ui = Ui_GeneratorTab()
        self.ui.setupUi(self)

        self.encoders = encoders
        self.modulated_scene_is_locked = False

        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.modulators = [Modulator("Modulation")]
        """:type: list of Modulator """

        self.has_default_modulation = True

        self.table_model = GeneratorTableModel(
            compare_frame_controller.proto_tree_model.rootItem,
            self.modulators, self.encoders)
        """:type: GeneratorTableModel """
        self.table_model.controller = self
        self.ui.tableBlocks.setModel(self.table_model)

        self.label_list_model = GeneratorListModel(self.table_model.protocol)
        self.ui.listViewProtoLabels.setModel(self.label_list_model)

        self.refresh_modulators()
        self.on_selected_modulation_changed()
        self.set_fuzzing_ui_status()
        self.project_manager = project_manager
        self.ui.prBarGeneration.hide()
        self.create_connects(compare_frame_controller)

    def create_connects(self, compare_frame_controller):
        compare_frame_controller.proto_tree_model.layoutChanged.connect(
            self.refresh_tree)
        self.ui.btnEditModulation.clicked.connect(self.show_modulation_dialog)
        self.ui.cBoxModulations.currentIndexChanged.connect(
            self.on_selected_modulation_changed)
        self.ui.tableBlocks.selectionModel().selectionChanged.connect(
            self.on_table_selection_changed)
        self.table_model.undo_stack.indexChanged.connect(self.refresh_table)
        self.table_model.undo_stack.indexChanged.connect(
            self.refresh_pause_list)
        self.table_model.undo_stack.indexChanged.connect(
            self.refresh_label_list)
        self.table_model.undo_stack.indexChanged.connect(
            self.refresh_estimated_time)
        self.table_model.undo_stack.indexChanged.connect(
            self.set_fuzzing_ui_status)
        self.table_model.protocol.qt_signals.line_duplicated.connect(
            self.refresh_pause_list)
        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.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.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.ui.listViewProtoLabels.editActionTriggered.connect(
            self.show_fuzzing_dialog)
        self.ui.listViewProtoLabels.editAllActionTriggered.connect(
            self.show_epic_fuzzing_dialog)
        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.tableBlocks.create_fuzzing_label_clicked.connect(
            self.create_fuzzing_label)
        self.ui.tableBlocks.edit_fuzzing_label_clicked.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)

    @property
    def active_groups(self):
        return self.tree_model.groups

    @pyqtSlot()
    def refresh_tree(self):
        #self.tree_model.reset()
        self.tree_model.layoutChanged.emit()
        self.ui.treeProtocols.expandAll()

    @pyqtSlot()
    def refresh_table(self):
        self.table_model.update()
        self.ui.tableBlocks.resize_it()
        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.layoutChanged.emit()

    @property
    def generator_undo_stack(self) -> QUndoStack:
        return self.table_model.undo_stack

    def refresh_modulators(self):
        current_index = 0
        if type(self.sender()) == ModulatorDialogController:
            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)

    @pyqtSlot()
    def on_selected_modulation_changed(self):
        cur_ind = self.ui.cBoxModulations.currentIndex()
        min_row, max_row, _, _ = self.ui.tableBlocks.selection_range()
        if min_row > -1:
            # Modulation für Selektierte Blöcke setzen
            for row in range(min_row, max_row + 1):
                try:
                    self.table_model.protocol.blocks[
                        row].modulator_indx = cur_ind
                except IndexError:
                    continue

        self.show_modulation_info()

    def show_modulation_info(self):

        show = not self.has_default_modulation or self.modulators[
            0] != Modulator("Modulation")

        if not show:
            self.ui.btnEditModulation.setStyleSheet("background: orange")
            font = QFont()
            font.setBold(True)
            self.ui.btnEditModulation.setFont(font)
        else:
            self.ui.btnEditModulation.setStyleSheet("")
            self.ui.btnEditModulation.setFont(QFont())

        cur_ind = self.ui.cBoxModulations.currentIndex()
        cur_mod = self.modulators[cur_ind]
        self.ui.lCarrierFreqValue.setText(cur_mod.carrier_frequency_str)
        self.ui.lCarrierPhaseValue.setText(cur_mod.carrier_phase_str)
        self.ui.lBitLenValue.setText(cur_mod.bit_len_str)
        self.ui.lSampleRateValue.setText(cur_mod.sample_rate_str)
        mod_type = cur_mod.modulation_type_str
        self.ui.lModTypeValue.setText(mod_type)
        if mod_type == "ASK":
            prefix = "Amplitude"
        elif mod_type == "PSK":
            prefix = "Phase"
        elif mod_type == "FSK":
            prefix = "Frequency"
        else:
            prefix = "Unknown Modulation Type (This should not happen...)"

        self.ui.lParamForZero.setText(prefix + " for 0:")
        self.ui.lParamForZeroValue.setText(cur_mod.param_for_zero_str)
        self.ui.lParamForOne.setText(prefix + " for 1:")
        self.ui.lParamForOneValue.setText(cur_mod.param_for_one_str)

    @pyqtSlot()
    def show_modulation_dialog(self):
        preselected_index = self.ui.cBoxModulations.currentIndex()
        min_row, max_row, start, end = self.ui.tableBlocks.selection_range()
        if min_row > -1:
            try:
                block = self.table_model.protocol.blocks[min_row]
                preselected_index = block.modulator_indx
            except IndexError:
                block = ProtocolBlock([True, False, True, False], 0, [])
        else:
            block = ProtocolBlock([True, False, True, False], 0, [])
            if len(self.table_model.protocol.blocks) > 0:
                block.bit_len = self.table_model.protocol.blocks[0].bit_len

        for m in self.modulators:
            m.default_sample_rate = self.project_manager.sample_rate

        c = ModulatorDialogController(self.modulators, parent=self)
        c.ui.treeViewSignals.setModel(self.tree_model)
        c.ui.treeViewSignals.expandAll()
        c.ui.comboBoxCustomModulations.setCurrentIndex(preselected_index)
        # c.ui.spinBoxBitLength.setValue(block.bit_len) Overrides Modulators value

        c.finished.connect(self.refresh_modulators)
        c.finished.connect(self.refresh_pause_list)
        c.show()
        bits = block.encoded_bits_str[0:10]
        c.original_bits = bits
        c.ui.linEdDataBits.setText(bits)
        c.ui.gVOriginalSignal.signal_tree_root = self.tree_model.rootItem
        c.draw_original_signal()
        c.ui.gVModulated.draw_full()
        c.ui.gVData.draw_full()
        c.ui.gVCarrier.draw_full()

        self.has_default_modulation = False

    @pyqtSlot()
    def on_table_selection_changed(self):
        min_row, max_row, start, end = self.ui.tableBlocks.selection_range()

        if min_row == -1:
            self.ui.lEncodingValue.setText("-")  #
            self.ui.lEncodingValue.setToolTip("")
            return

        container = self.table_model.protocol
        block = container.blocks[min_row]
        decoder_name = block.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(block.modulator_indx)
        self.show_modulation_info()
        self.ui.cBoxModulations.blockSignals(False)

    @pyqtSlot(int)
    def edit_pause_item(self, index: int):
        block = self.table_model.protocol.blocks[index]
        cur_len = block.pause
        new_len, ok = QInputDialog.getInt(self,
                                          self.tr("Enter new Pause Length"),
                                          self.tr("Pause Length:"), cur_len, 0)
        if ok:
            block.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.modulators[
                self.table_model.protocol.blocks[i].modulator_indx].sample_rate
            item = fmt_str.format(pause, i + 1, i + 2,
                                  Formatter.science_time(pause / sr))
            self.ui.lWPauses.addItem(item)

    @pyqtSlot()
    def on_lWpauses_selection_changed(self):
        rows = [index.row() for index in self.ui.lWPauses.selectedIndexes()]
        if len(rows) == 0:
            return
        self.ui.tableBlocks.show_pause_active = True
        self.ui.tableBlocks.pause_row = rows[0]
        self.ui.tableBlocks.viewport().update()
        self.ui.tableBlocks.scrollTo(self.table_model.index(rows[0], 0))

    @pyqtSlot()
    def on_lWPauses_lost_focus(self):
        self.ui.tableBlocks.show_pause_active = False
        self.ui.tableBlocks.viewport().update()

    @pyqtSlot()
    def generate_file(self):
        modulated_samples = self.modulate_data()
        FileOperator.save_data_dialog("", modulated_samples, parent=self)

    def modulate_data(self):
        pos = 0
        modulated_samples = []
        container = self.table_model.protocol
        self.ui.prBarGeneration.show()
        self.ui.prBarGeneration.setValue(0)
        self.ui.prBarGeneration.setMaximum(self.table_model.row_count)
        for i in range(0, self.table_model.row_count):
            block = container.blocks[i]
            modulator = self.modulators[block.modulator_indx]
            modulator.modulate(start=pos,
                               data=block.encoded_bits,
                               pause=block.pause)
            modulated_samples.append(modulator.modulated_samples)
            pos += len(modulator.modulated_samples)
            self.ui.prBarGeneration.setValue(i + 1)
            QApplication.processEvents()

        self.ui.prBarGeneration.hide()
        return numpy.concatenate(modulated_samples).astype(numpy.complex64)

    @pyqtSlot(int)
    def show_fuzzing_dialog(self, label_index: int):
        view = self.ui.cbViewType.currentIndex()
        fdc = FuzzingDialogController(self.table_model.protocol,
                                      label_index,
                                      view,
                                      parent=self)
        fdc.show()
        fdc.finished.connect(self.refresh_label_list)
        fdc.finished.connect(self.refresh_table)
        fdc.finished.connect(self.set_fuzzing_ui_status)

    @pyqtSlot()
    def show_epic_fuzzing_dialog(self):
        view = self.ui.cbViewType.currentIndex()
        fdc = FuzzingDialogController(self.table_model.protocol,
                                      0,
                                      view,
                                      parent=self)
        fdc.enter_epic_mode()
        fdc.show()
        fdc.finished.connect(self.refresh_label_list)
        fdc.finished.connect(self.refresh_table)
        fdc.finished.connect(self.set_fuzzing_ui_status)

    @pyqtSlot()
    def handle_plabel_fuzzing_state_changed(self):
        self.refresh_table()
        self.label_list_model.layoutChanged.emit()

    @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"

        fuzz_action = Fuzz(self.table_model.protocol, fuz_mode)
        self.table_model.undo_stack.push(fuzz_action)

    @pyqtSlot()
    def set_fuzzing_ui_status(self):
        btn_was_enabled = self.ui.btnFuzz.isEnabled()
        pac = self.table_model.protocol
        self.ui.btnFuzz.setEnabled(len(pac.active_fuzzing_labels) > 0)
        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_block = pac.has_fuzz_labels_with_same_block
        self.ui.rBSuccessive.setEnabled(has_same_block)
        self.ui.rBExhaustive.setEnabled(has_same_block)
        self.ui.rbConcurrent.setEnabled(has_same_block)

    @pyqtSlot()
    def refresh_estimated_time(self):
        c = self.table_model.protocol
        if c.num_blocks == 0:
            self.ui.lEstimatedTime.setText("Estimated Time: ")
            return

        avg_block_len = numpy.mean(
            [len(block.encoded_bits) for block in c.blocks])
        avg_bit_len = numpy.mean([m.samples_per_bit 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_blocks * avg_block_len * avg_bit_len + 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, refblock: int, start: int, end: int):
        con = self.table_model.protocol
        start, end = con.convert_range(start, end - 1,
                                       self.ui.cbViewType.currentIndex(), 0,
                                       False, refblock)
        lbl = con.create_fuzzing_label(start, end, refblock)
        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)

        label = self.table_model.protocol.protocol_labels[maxrow]
        if not label.show:
            return
        start, end = self.table_model.protocol.get_label_range(
            label, self.table_model.proto_view, False)

        # start = int(label.start / factor)
        # end = math.ceil(label.end / factor)
        indx = self.table_model.index(0, int((start + end) / 2))

        self.ui.tableBlocks.scrollTo(indx)

    @pyqtSlot()
    def on_view_type_changed(self):
        self.table_model.proto_view = self.ui.cbViewType.currentIndex()
        self.table_model.update()
        self.ui.tableBlocks.resize_it()

    def refresh_protocol_labels(self):
        self.table_model.protocol.refresh_protolabel_blocks()

    def close_all(self):
        self.tree_model.rootItem.clearChilds()
        self.tree_model.rootItem.addGroup()
        self.table_model.protocol.clear()
        self.table_model.clear()
        self.refresh_tree()
        self.refresh_table()
        self.refresh_label_list()

    @pyqtSlot()
    def on_btn_send_clicked(self):
        modulated_data = self.modulate_data()
        dialog = SendRecvDialogController(self.project_manager.frequency,
                                          self.project_manager.sample_rate,
                                          self.project_manager.bandwidth,
                                          self.project_manager.gain,
                                          self.project_manager.device,
                                          Mode.send,
                                          modulated_data=modulated_data,
                                          parent=self)
        if dialog.has_empty_device_list:
            Errors.no_device()
            dialog.close()
            return

        dialog.recording_parameters.connect(
            self.project_manager.set_recording_parameters)
        dialog.show()