diff --git a/README.md b/README.md index 9e529ab1..e1320633 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ sudo python setup.py install ``` # Running from source -To run urh without installation, just run: +To execute urh without installation, just run: ```bash cd bin ./urh diff --git a/src/urh/FFTSceneManager.py b/src/urh/FFTSceneManager.py index d3f956e1..c8fbc105 100644 --- a/src/urh/FFTSceneManager.py +++ b/src/urh/FFTSceneManager.py @@ -35,8 +35,9 @@ class FFTSceneManager(SceneManager): self.path_item.setPath(path) try: - peak_path = path_creator.create_path(np.log10(self.peak), start, end) - self.peak_item.setPath(peak_path) + if len(self.peak) > 0: + peak_path = path_creator.create_path(np.log10(self.peak), start, end) + self.peak_item.setPath(peak_path) except RuntimeWarning: pass diff --git a/src/urh/controller/CompareFrameController.py b/src/urh/controller/CompareFrameController.py index 4a53c044..bd8677b5 100644 --- a/src/urh/controller/CompareFrameController.py +++ b/src/urh/controller/CompareFrameController.py @@ -11,8 +11,7 @@ from PyQt5.QtWidgets import QMessageBox, QFrame, QAbstractItemView, QUndoStack, from urh import constants from urh.controller.OptionsController import OptionsController from urh.controller.ProtocolLabelController import ProtocolLabelController -from urh.controller.ProtocolSniffDialogController import \ - ProtocolSniffDialogController +from urh.controller.ProtocolSniffDialogController import ProtocolSniffDialogController from urh.models.LabelValueTableModel import LabelValueTableModel from urh.models.ProtocolLabelListModel import ProtocolLabelListModel from urh.models.ProtocolTableModel import ProtocolTableModel @@ -57,6 +56,7 @@ class CompareFrameController(QFrame): self.decimal_point = clocale.decimalPoint() self.__active_group_ids = [0] + self.selected_protocols = set() self.protocol_model = ProtocolTableModel(self.proto_analyzer, self) """:type: ProtocolTableModel""" @@ -665,6 +665,7 @@ class CompareFrameController(QFrame): active_group_ids = set() selection = QItemSelection() + self.selected_protocols.clear() for group, tree_items in self.proto_tree_model.protocol_tree_items.items(): for i, tree_item in enumerate(tree_items): @@ -673,11 +674,9 @@ class CompareFrameController(QFrame): #index = self.proto_tree_model.createIndex(i, 0, tree_item) #selection.select(index, index) active_group_ids.add(group) + self.selected_protocols.add(proto) - if active_group_ids == set(self.active_group_ids): - ignore_table_model_on_update = True - else: - ignore_table_model_on_update = False + if active_group_ids != set(self.active_group_ids): self.active_group_ids = list(active_group_ids) self.active_group_ids.sort() @@ -698,7 +697,7 @@ class CompareFrameController(QFrame): self.ui.treeViewProtocols.selectionModel().select(selection, QItemSelectionModel.ClearAndSelect) self.ui.treeViewProtocols.blockSignals(False) - self.updateUI(ignore_table_model=ignore_table_model_on_update, resize_table=False) + self.updateUI(ignore_table_model=True, resize_table=False) @pyqtSlot(int) def handle_ref_index_changed(self, new_ref_index): diff --git a/src/urh/controller/GeneratorTabController.py b/src/urh/controller/GeneratorTabController.py index 5a91cfde..0a6eda2d 100644 --- a/src/urh/controller/GeneratorTabController.py +++ b/src/urh/controller/GeneratorTabController.py @@ -172,7 +172,7 @@ class GeneratorTabController(QWidget): prefix = "Amplitude" elif mod_type == "PSK": prefix = "Phase" - elif mod_type == "FSK": + elif mod_type in ("FSK", "GFSK"): prefix = "Frequency" else: prefix = "Unknown Modulation Type (This should not happen...)" diff --git a/src/urh/controller/MainController.py b/src/urh/controller/MainController.py index 9e3a9926..1fc63247 100644 --- a/src/urh/controller/MainController.py +++ b/src/urh/controller/MainController.py @@ -31,7 +31,6 @@ from urh.util.ProjectManager import ProjectManager class MainController(QMainWindow): - resized = pyqtSignal() def __init__(self, *args): super().__init__(*args) @@ -343,7 +342,6 @@ class MainController(QMainWindow): sframe.refresh(draw_full_signal=True) # Hier wird das Protokoll ausgelesen sframe.ui.gvSignal.autofit_view() - self.resized.connect(sframe.redraw_after_resize) self.set_frame_numbers() self.ui.progressBar.setValue(99) QApplication.processEvents() @@ -383,10 +381,6 @@ class MainController(QMainWindow): self.set_frame_numbers() self.refresh_main_menu() - def resizeEvent(self, event: QResizeEvent): - event.accept() - self.resized.emit() - def updateRecentActionList(self): recentFilePaths = constants.SETTINGS.value("recentFiles") recentFilePaths = [p for p in recentFilePaths if os.path.exists(p)] if recentFilePaths else [] @@ -435,7 +429,7 @@ class MainController(QMainWindow): @pyqtSlot() def show_about(self): - QMessageBox.about(self, self.tr("About"), self.tr("Version: {0}").format(version.VERSION)) + QMessageBox.about(self, self.tr("About"), self.tr("

Universal Radio Hacker

Version: {0}
GitHub: https://github.com/jopohl/urh

Contributors:").format(version.VERSION)) @pyqtSlot(CompareFrameController, int, int, int, int) def show_protocol_selection_in_interpretation(self, startblock, start, endblock, end): diff --git a/src/urh/controller/ModulatorDialogController.py b/src/urh/controller/ModulatorDialogController.py index 7288f7b0..9c2e9a6c 100644 --- a/src/urh/controller/ModulatorDialogController.py +++ b/src/urh/controller/ModulatorDialogController.py @@ -27,13 +27,8 @@ class ModulatorDialogController(QDialog): self.modulators = modulators + self.set_ui_for_current_modulator() - self.ui.doubleSpinBoxCarrierFreq.setValue(self.current_modulator.carrier_freq_hz) - self.ui.doubleSpinBoxCarrierPhase.setValue(self.current_modulator.carrier_phase_deg) - self.ui.spinBoxBitLength.setValue(self.current_modulator.samples_per_bit) - self.ui.spinBoxSampleRate.setValue(self.current_modulator.sample_rate) - self.ui.spinBoxParameter0.setValue(self.current_modulator.param_for_zero) - self.ui.spinBoxParameter1.setValue(self.current_modulator.param_for_one) self.ui.cbShowDataBitsOnly.setText(self.tr("Show Only Data Sequence\n")) self.ui.cbShowDataBitsOnly.setEnabled(False) self.protocol = None @@ -45,7 +40,7 @@ class ModulatorDialogController(QDialog): self.ui.chkBoxLockSIV.setDisabled(True) - self.ui.comboBoxModulationType.setCurrentIndex(self.current_modulator.modulation_type) + self.create_connects() self.on_modulation_type_changed() @@ -58,14 +53,25 @@ class ModulatorDialogController(QDialog): mod = self.modulators[self.ui.comboBoxCustomModulations.currentIndex()] return mod + def set_ui_for_current_modulator(self): + index = self.ui.comboBoxModulationType.findText("*("+self.current_modulator.modulation_type_str+")", Qt.MatchWildcard) + self.ui.comboBoxModulationType.setCurrentIndex(index) + self.ui.doubleSpinBoxCarrierFreq.setValue(self.current_modulator.carrier_freq_hz) + self.ui.doubleSpinBoxCarrierPhase.setValue(self.current_modulator.carrier_phase_deg) + self.ui.spinBoxBitLength.setValue(self.current_modulator.samples_per_bit) + self.ui.spinBoxSampleRate.setValue(self.current_modulator.sample_rate) + self.ui.spinBoxParameter0.setValue(self.current_modulator.param_for_zero) + self.ui.spinBoxParameter1.setValue(self.current_modulator.param_for_one) + + def create_connects(self): - self.ui.doubleSpinBoxCarrierFreq.valueChanged.connect(self.on_carrier_freq_changed) - self.ui.doubleSpinBoxCarrierPhase.valueChanged.connect(self.on_carrier_phase_changed) - self.ui.spinBoxBitLength.valueChanged.connect(self.on_bit_len_changed) - self.ui.spinBoxSampleRate.valueChanged.connect(self.on_sample_rate_changed) + self.ui.doubleSpinBoxCarrierFreq.editingFinished.connect(self.on_carrier_freq_changed) + self.ui.doubleSpinBoxCarrierPhase.editingFinished.connect(self.on_carrier_phase_changed) + self.ui.spinBoxBitLength.editingFinished.connect(self.on_bit_len_changed) + self.ui.spinBoxSampleRate.editingFinished.connect(self.on_sample_rate_changed) self.ui.linEdDataBits.textChanged.connect(self.on_data_bits_changed) - self.ui.spinBoxParameter0.valueChanged.connect(self.on_modulation_parameter_zero_changed) - self.ui.spinBoxParameter1.valueChanged.connect(self.on_modulation_parameter_one_changed) + self.ui.spinBoxParameter0.editingFinished.connect(self.on_modulation_parameter_zero_changed) + self.ui.spinBoxParameter1.editingFinished.connect(self.on_modulation_parameter_one_changed) self.ui.comboBoxModulationType.currentIndexChanged.connect(self.on_modulation_type_changed) self.ui.gVOriginalSignal.zoomed.connect(self.on_orig_signal_zoomed) self.ui.cbShowDataBitsOnly.stateChanged.connect(self.on_show_data_bits_only_changed) @@ -80,6 +86,8 @@ class ModulatorDialogController(QDialog): self.ui.gVData.zoomed.connect(self.on_carrier_data_modulated_zoomed) self.ui.gVModulated.sel_area_width_changed.connect(self.on_modulated_selection_changed) self.ui.gVOriginalSignal.sel_area_width_changed.connect(self.on_original_selection_changed) + self.ui.spinBoxGaussBT.editingFinished.connect(self.on_gauss_bt_edited) + self.ui.spinBoxGaussFilterWidth.editingFinished.connect(self.on_gaus_filter_wdith_edited) self.ui.chkBoxLockSIV.stateChanged.connect(self.on_lock_siv_changed) self.ui.btnRestoreBits.clicked.connect(self.on_btn_restore_bits_clicked) @@ -166,6 +174,11 @@ class ModulatorDialogController(QDialog): else: self.ui.btnRestoreBits.setEnabled(True) + def __cur_selected_mod_type(self): + s = self.ui.comboBoxModulationType.currentText() + return s[s.rindex("(")+1:s.rindex(")")] + + @pyqtSlot() def on_sample_rate_changed(self): if int(self.ui.spinBoxSampleRate.value()) > 0: @@ -183,15 +196,27 @@ class ModulatorDialogController(QDialog): self.current_modulator.param_for_one = self.ui.spinBoxParameter1.value() self.draw_modulated() + @pyqtSlot() + def on_gauss_bt_edited(self): + self.current_modulator.gauss_bt = self.ui.spinBoxGaussBT.value() + self.draw_modulated() + + @pyqtSlot() + def on_gaus_filter_wdith_edited(self): + self.current_modulator.gauss_filter_width = self.ui.spinBoxGaussFilterWidth.value() + self.draw_modulated() + @pyqtSlot() def on_modulation_type_changed(self): - if self.current_modulator.modulation_type == self.ui.comboBoxModulationType.currentIndex(): + if self.current_modulator.modulation_type_str == self.__cur_selected_mod_type(): write_standard_parameters = False else: - self.current_modulator.modulation_type = self.ui.comboBoxModulationType.currentIndex() + self.current_modulator.modulation_type_str = self.__cur_selected_mod_type() write_standard_parameters = True - if self.ui.comboBoxModulationType.currentIndex() == 0: + self.__set_gauss_ui_visibility(self.__cur_selected_mod_type() == "GFSK") + + if self.__cur_selected_mod_type() == "ASK": self.ui.lParameterfor0.setText(self.tr("Amplitude for 0:")) self.ui.lParameterfor1.setText(self.tr("Amplitude for 1:")) self.ui.spinBoxParameter0.auto_suffix = False @@ -211,7 +236,7 @@ class ModulatorDialogController(QDialog): self.ui.spinBoxParameter0.setValue(self.current_modulator.param_for_zero) self.ui.spinBoxParameter1.setValue(self.current_modulator.param_for_one) - elif self.ui.comboBoxModulationType.currentIndex() == 1: + elif self.__cur_selected_mod_type() in ("FSK", "GFSK"): self.ui.spinBoxParameter0.auto_suffix = True self.ui.spinBoxParameter1.auto_suffix = True self.ui.lParameterfor0.setText(self.tr("Frequency for 0:")) @@ -219,18 +244,16 @@ class ModulatorDialogController(QDialog): self.ui.spinBoxParameter0.setMaximum(1e12) self.ui.spinBoxParameter0.setMinimum(-1e12) self.ui.spinBoxParameter0.setDecimals(4) - self.ui.spinBoxParameter0.setSuffix("") self.ui.spinBoxParameter1.setMaximum(1e12) self.ui.spinBoxParameter1.setMinimum(-1e12) self.ui.spinBoxParameter1.setDecimals(4) - self.ui.spinBoxParameter1.setSuffix("") if write_standard_parameters: self.autodetect_fsk_freqs() else: self.ui.spinBoxParameter0.setValue(self.current_modulator.param_for_zero) self.ui.spinBoxParameter1.setValue(self.current_modulator.param_for_one) - elif self.ui.comboBoxModulationType.currentIndex() == 2: + elif self.__cur_selected_mod_type() == "PSK": self.ui.spinBoxParameter0.auto_suffix = False self.ui.spinBoxParameter1.auto_suffix = False self.ui.lParameterfor0.setText(self.tr("Phase (degree) for 0:")) @@ -250,7 +273,8 @@ class ModulatorDialogController(QDialog): self.ui.spinBoxParameter0.setValue(self.current_modulator.param_for_zero) self.ui.spinBoxParameter1.setValue(self.current_modulator.param_for_one) - self.draw_modulated() + self.ui.spinBoxParameter0.editingFinished.emit() + self.ui.spinBoxParameter1.editingFinished.emit() def resizeEvent(self, event: QResizeEvent): @@ -327,6 +351,7 @@ class ModulatorDialogController(QDialog): @pyqtSlot() def on_custom_modulation_index_changed(self): + self.set_ui_for_current_modulator() self.draw_carrier() self.draw_data_bits() self.draw_modulated() @@ -472,7 +497,7 @@ class ModulatorDialogController(QDialog): self.autodetect_fsk_freqs() def autodetect_fsk_freqs(self): - if self.ui.comboBoxModulationType.currentIndex() != 1: + if self.__cur_selected_mod_type() not in ("FSK", "GFSK"): return try: @@ -530,3 +555,13 @@ class ModulatorDialogController(QDialog): @pyqtSlot(int) def on_original_selection_changed(self, new_width: int): self.ui.lOriginalSignalSamplesSelected.setText(str(abs(new_width))) + + + def __set_gauss_ui_visibility(self, show:bool): + self.ui.lGaussBT.setVisible(show) + self.ui.lGaussWidth.setVisible(show) + self.ui.spinBoxGaussBT.setVisible(show) + self.ui.spinBoxGaussFilterWidth.setVisible(show) + + self.ui.spinBoxGaussFilterWidth.setValue(self.current_modulator.gauss_filter_width) + self.ui.spinBoxGaussBT.setValue(self.current_modulator.gauss_bt) \ No newline at end of file diff --git a/src/urh/controller/ProtocolLabelController.py b/src/urh/controller/ProtocolLabelController.py index 0a00d44b..69ca880c 100644 --- a/src/urh/controller/ProtocolLabelController.py +++ b/src/urh/controller/ProtocolLabelController.py @@ -1,6 +1,6 @@ import numpy -from PyQt5.QtCore import Qt, pyqtSlot -from PyQt5.QtWidgets import QDialog, QApplication +from PyQt5.QtCore import Qt, pyqtSlot, QModelIndex +from PyQt5.QtWidgets import QDialog, QApplication, QInputDialog from urh import constants from urh.models.PLabelTableModel import PLabelTableModel @@ -30,9 +30,6 @@ class ProtocolLabelController(QDialog): self.ui.tblViewProtoLabels.setItemDelegateForColumn(1, SpinBoxDelegate(1, maxval, self)) self.ui.tblViewProtoLabels.setItemDelegateForColumn(2, SpinBoxDelegate(1, maxval, self)) self.ui.tblViewProtoLabels.setItemDelegateForColumn(3, CheckBoxDelegate(self)) - self.ui.tblViewProtoLabels.setItemDelegateForColumn(4, SpinBoxDelegate(offset+1, - offset+proto_group.num_blocks, - self)) self.ui.tblViewProtoLabels.setItemDelegateForColumn(5, ComboBoxDelegate([""] * len(constants.LABEL_COLORS), True, @@ -57,6 +54,7 @@ class ProtocolLabelController(QDialog): self.ui.btnConfirm.clicked.connect(self.confirm) self.ui.cbProtoView.currentIndexChanged.connect(self.set_view_index) self.model.restrictive_changed.connect(self.handle_restrictive_changed) + self.ui.tblViewProtoLabels.clicked.connect(self.on_table_clicked) @pyqtSlot() def confirm(self): @@ -67,21 +65,30 @@ class ProtocolLabelController(QDialog): self.ui.tblViewProtoLabels.openPersistentEditor(self.model.index(row, 2)) self.ui.tblViewProtoLabels.openPersistentEditor(self.model.index(row, 3)) - if self.model.protocol_labels[row].restrictive: - self.ui.tblViewProtoLabels.openPersistentEditor(self.model.index(row, 4)) - self.ui.tblViewProtoLabels.openPersistentEditor(self.model.index(row, 5)) self.ui.tblViewProtoLabels.openPersistentEditor(self.model.index(row, 6)) self.ui.tblViewProtoLabels.openPersistentEditor(self.model.index(row, 7)) @pyqtSlot(int, bool) def handle_restrictive_changed(self, row: int, restrictive: bool): - if restrictive: - self.ui.tblViewProtoLabels.openPersistentEditor(self.model.index(row, 4)) - else: - self.ui.tblViewProtoLabels.closePersistentEditor(self.model.index(row, 4)) + self.model.update() @pyqtSlot(int) def set_view_index(self, ind): self.model.proto_view = ind - self.model.update() \ No newline at end of file + self.model.update() + + @pyqtSlot(QModelIndex) + def on_table_clicked(self, index: QModelIndex): + if not index.isValid(): + return + + i = index.row() + lbl = self.model.protocol_labels[i] + j = index.column() + if j == 4 and lbl.restrictive: + seqs, indexes = self.model.get_protocol_sequences(lbl.start, lbl.end) + item, ok = QInputDialog.getItem(self, "Choose matching pattern", "Pattern", seqs) + if ok and item: + self.model.set_refblock(lbl, indexes[seqs.index(item)]) + self.model.update() diff --git a/src/urh/controller/SendRecvDialogController.py b/src/urh/controller/SendRecvDialogController.py index a5ae1f29..f7414ecf 100644 --- a/src/urh/controller/SendRecvDialogController.py +++ b/src/urh/controller/SendRecvDialogController.py @@ -158,6 +158,7 @@ class SendRecvDialogController(QDialog): self.device.frequency = self.ui.spinBoxFreq.value() if self.mode == Mode.spectrum: self.scene_creator.scene.center_freq = self.ui.spinBoxFreq.value() + self.scene_creator.clear_path() @pyqtSlot() def on_bw_changed(self): diff --git a/src/urh/controller/SignalFrameController.py b/src/urh/controller/SignalFrameController.py index 26450611..12bfbed6 100644 --- a/src/urh/controller/SignalFrameController.py +++ b/src/urh/controller/SignalFrameController.py @@ -1021,13 +1021,6 @@ class SignalFrameController(QFrame): self.ui.btnSaveSignal.hide() self.ui.lineEditSignalName.setFont(font) - def redraw_after_resize(self): - if self.ui.gvSignal.view_rect().width() > self.ui.gvSignal.sceneRect().width(): - x_factor = self.ui.gvSignal.width() / self.ui.gvSignal.sceneRect().width() - self.ui.gvSignal.scale(x_factor / self.ui.gvSignal.transform().m11(), 1) - - self.ui.gvSignal.autofit_view() - def contextMenuEvent(self, event: QContextMenuEvent): if self.signal is None: return diff --git a/src/urh/controller/SignalTabController.py b/src/urh/controller/SignalTabController.py index 4fd373ff..1c338860 100644 --- a/src/urh/controller/SignalTabController.py +++ b/src/urh/controller/SignalTabController.py @@ -148,7 +148,6 @@ class SignalTabController(QWidget): sig_frame.files_dropped.connect(self.handle_files_dropped) sig_frame.apply_to_all_clicked.connect(self.handle_apply_to_all_clicked) sig_frame.sort_action_clicked.connect(self.sort_frames_by_name) - self.splitter.splitterMoved.connect(sig_frame.redraw_after_resize) if prev_signal_frame is not None: diff --git a/src/urh/models/PLabelTableModel.py b/src/urh/models/PLabelTableModel.py index 2fbf66c6..e3adea2a 100644 --- a/src/urh/models/PLabelTableModel.py +++ b/src/urh/models/PLabelTableModel.py @@ -7,7 +7,7 @@ from urh.signalprocessing.ProtocolGroup import ProtocolGroup class PLabelTableModel(QAbstractTableModel): header_labels = ["Name", "Start", "End", 'Match exactly', - "Matching Block", 'Color', 'Apply decoding', 'Delete'] + "Matching sequence", 'Color', 'Apply decoding', 'Delete'] restrictive_changed = pyqtSignal(int, bool) label_removed = pyqtSignal(ProtocolLabel) @@ -56,7 +56,15 @@ class PLabelTableModel(QAbstractTableModel): return lbl.restrictive elif j == 4: if lbl.restrictive: - return lbl.refblock + self.offset + 1 + start = int(self.proto_group.convert_index(lbl.start, 0, self.proto_view, True)[0]) + end = int(self.proto_group.convert_index(lbl.end, 0, self.proto_view, True)[1]) + block = self.proto_group.blocks[lbl.refblock] + if self.proto_view == 0: + return block.decoded_bits_str[start:end] + elif self.proto_view == 1: + return block.decoded_hex_str[start:end] + else: + return block.decoded_ascii_str[start:end] else: return "-" elif j == 5: @@ -99,9 +107,8 @@ class PLabelTableModel(QAbstractTableModel): self.restrictive_changed.emit(i, value) lbl.find_block_numbers(proto) elif j == 4: - lbl.refblock = int(value) - self.offset - 1 - lbl.reference_bits = proto[lbl.refblock][lbl.start:lbl.end] - lbl.find_block_numbers(proto) + # Pass Reference bits via seperate dialog + pass elif j == 5: lbl.color_index = value elif j == 6: @@ -111,6 +118,13 @@ class PLabelTableModel(QAbstractTableModel): return True + def set_refblock(self, lbl: ProtocolLabel, refblock: int): + proto = self.proto_group.decoded_bits_str + lbl.refblock = int(refblock) - self.offset + lbl.reference_bits = proto[lbl.refblock][lbl.start:lbl.end] + lbl.find_block_numbers(proto) + + def flags(self, index): if not index.isValid(): return Qt.NoItemFlags @@ -121,11 +135,35 @@ class PLabelTableModel(QAbstractTableModel): return Qt.NoItemFlags if index.column() == 4 and not lbl.restrictive: - return Qt.ItemIsSelectable + return Qt.NoItemFlags + elif index.column() == 4 and lbl.restrictive: + return Qt.ItemIsSelectable | Qt.ItemIsEnabled return Qt.ItemIsEditable | Qt.ItemIsEnabled def remove_label(self, label): self.proto_group.remove_label(label) self.update() - self.label_removed.emit(label) \ No newline at end of file + self.label_removed.emit(label) + + def get_protocol_sequences(self, start: int, end: int): + sequences = [] + indexes = [] + start = int(self.proto_group.convert_index(start, 0, self.proto_view, True)[0]) + end = int(self.proto_group.convert_index(end, 0, self.proto_view, True)[0]) + + for i, block in enumerate(self.proto_group.blocks): + + if self.proto_view == 0: + data = block.decoded_bits_str[start:end] + elif self.proto_view == 1: + data = block.decoded_hex_str[start:end] + else: + data = block.decoded_ascii_str[start:end] + + if len(data) == end - start and data not in sequences: + sequences.append(data) + indexes.append(i) # For setting refblock later + + return sequences, indexes + diff --git a/src/urh/models/ProtocolTreeModel.py b/src/urh/models/ProtocolTreeModel.py index 0e5b30da..0e101480 100644 --- a/src/urh/models/ProtocolTreeModel.py +++ b/src/urh/models/ProtocolTreeModel.py @@ -121,11 +121,15 @@ class ProtocolTreeModel(QAbstractItemModel): return QIcon.fromTheme("folder") elif role == Qt.CheckStateRole: return item.show - elif role == Qt.FontRole and item.is_group and\ - self.rootItem.index_of(item) in self.controller.active_group_ids: - font = QFont() - font.setBold(True) - return font + elif role == Qt.FontRole: + if item.is_group and self.rootItem.index_of(item) in self.controller.active_group_ids: + font = QFont() + font.setBold(True) + return font + elif item.protocol in self.controller.selected_protocols: + font = QFont() + font.setBold(True) + return font elif role == Qt.TextColorRole and item.protocol == self.reference_protocol: return constants.SELECTED_ROW_COLOR elif role == Qt.ToolTipRole: diff --git a/src/urh/models/TableModel.py b/src/urh/models/TableModel.py index 647ca085..9d55c4b7 100644 --- a/src/urh/models/TableModel.py +++ b/src/urh/models/TableModel.py @@ -120,14 +120,13 @@ class TableModel(QAbstractTableModel): offset = 0 for group in self.controller.groups: - if group in self.controller.active_groups: - for lbl in group.labels: - bg_color = label_colors[lbl.color_index] - for i in lbl.block_numbers: - start, end = group.get_label_range(lbl, self.proto_view, self.decode) - for j in range(start, end): - self.background_colors[i+offset, j] = bg_color - self.tooltips[i+offset, j] = lbl.name + for lbl in group.labels: + bg_color = label_colors[lbl.color_index] + for i in lbl.block_numbers: + start, end = group.get_label_range(lbl, self.proto_view, self.decode) + for j in range(start, end): + self.background_colors[i+offset, j] = bg_color + self.tooltips[i+offset, j] = lbl.name offset += group.num_blocks def refresh_fonts(self): diff --git a/src/urh/signalprocessing/Modulator.py b/src/urh/signalprocessing/Modulator.py index dab09e50..10d7e233 100644 --- a/src/urh/signalprocessing/Modulator.py +++ b/src/urh/signalprocessing/Modulator.py @@ -10,13 +10,13 @@ from urh.cythonext import path_creator from urh.cythonext.signalFunctions import Symbol from urh.ui.ZoomableScene import ZoomableScene - class Modulator(object): """ This class can modulate bits to a carrier. Very useful in generation phase. """ - MODULATION_TYPES = ["ASK", "FSK", "PSK"] + + MODULATION_TYPES = ["ASK", "FSK", "PSK", "GFSK"] def __init__(self, name: str): @@ -30,6 +30,9 @@ class Modulator(object): self.modulation_type = 0 self.name = name + self.gauss_bt = 0.5 # bt product for gaussian filter (GFSK) + self.gauss_filter_width = 1 # filter width for gaussian filter (GFSK) + self.param_for_zero = 0 # Freq, Amplitude (0..100%) or Phase (0..360) self.param_for_one = 100 # Freq, Amplitude (0..100%) or Phase (0..360) @@ -85,12 +88,18 @@ class Modulator(object): def modulation_type_str(self): return self.MODULATION_TYPES[self.modulation_type] + @modulation_type_str.setter + def modulation_type_str(self, val: str): + val = val.upper() + if val in self.MODULATION_TYPES: + self.modulation_type = self.MODULATION_TYPES.index(val) + @property def param_for_zero_str(self): mod = self.MODULATION_TYPES[self.modulation_type] if mod == "ASK": return str(self.param_for_zero) + "%" - elif mod == "FSK": + elif mod == "FSK" or mod == "GFSK": return self.get_value_with_suffix(self.param_for_zero) + "Hz" elif mod == "PSK": return str(self.param_for_zero) + "°" @@ -100,7 +109,7 @@ class Modulator(object): mod = self.MODULATION_TYPES[self.modulation_type] if mod == "ASK": return str(self.param_for_one) + "%" - elif mod == "FSK": + elif mod == "FSK" or mod == "GFSK": return self.get_value_with_suffix(self.param_for_one) + "Hz" elif mod == "PSK": return str(self.param_for_one) + "°" @@ -161,19 +170,57 @@ class Modulator(object): log_bit = bit samples_per_bit = self.samples_per_bit - param = self.param_for_one if log_bit else self.param_for_zero + if mod_type == "FSK" or mod_type == "GFSK": + param = 1 if log_bit else -1 + else: + param = self.param_for_one if log_bit else self.param_for_zero paramvector[sample_pos:sample_pos + samples_per_bit] = np.full(samples_per_bit, param, dtype=np.float64) sample_pos += samples_per_bit t = np.arange(start, start + total_samples - pause) / self.sample_rate a = paramvector / 100 if mod_type == "ASK" else self.carrier_amplitude phi = paramvector * (np.pi / 180) if mod_type == "PSK" else self.carrier_phase_deg * (np.pi / 180) - f = paramvector if mod_type == "FSK" else self.carrier_freq_hz + + if mod_type == "FSK" or mod_type == "GFSK": + fmid = (self.param_for_one + self.param_for_zero)/2 + dist = abs(fmid - self.param_for_one) + if mod_type == "GFSK": + gfir = self.gauss_fir(bt=self.gauss_bt, filter_width=self.gauss_filter_width) + if len(paramvector) >= len(gfir): + paramvector = np.convolve(paramvector, gfir, mode="same") + else: + # Prevent dimension crash later, because gaussian finite impulse response is longer then paramvector + paramvector = np.convolve(gfir, paramvector, mode="same")[:len(paramvector)] + + f = fmid + dist * paramvector + + # sin(2*pi*f_1*t_1 + phi_1) = sin(2*pi*f_2*t_1 + phi_2) <=> phi_2 = 2*pi*t_1*(f_1 - f_2) + phi_1 + phi = np.empty(len(f)) + phi[0] = self.carrier_phase_deg + for i in range(0, len(phi) - 1): + phi[i+1] = 2 * np.pi * t[i] * (f[i]-f[i+1]) + phi[i] # Correct the phase to prevent spiky jumps + else: + f = self.carrier_freq_hz self.modulated_samples.imag[:total_samples - pause] = a * np.sin(2 * np.pi * f * t + phi) self.modulated_samples.real[:total_samples - pause] = a * np.cos(2 * np.pi * f * t + phi) + def gauss_fir(self, bt=0.5, filter_width=1): + """ + + :param bt: normalized 3-dB bandwidth-symbol time product + :param span: filter span in symbols + :return: + """ + # http://onlinelibrary.wiley.com/doi/10.1002/9780470041956.app2/pdf + k = np.arange(-int(filter_width * self.samples_per_bit), int(filter_width * self.samples_per_bit)+1) + ts = self.samples_per_bit / self.sample_rate # symbol time + #a = np.sqrt(np.log(2)/2)*(ts/bt) + #B = a / np.sqrt(np.log(2)/2) # filter bandwidth + h = np.sqrt((2*np.pi)/(np.log(2))) * bt/ts * np.exp(-(((np.sqrt(2)*np.pi)/np.sqrt(np.log(2))*bt*k/self.samples_per_bit)**2)) + return h / h.sum() + @staticmethod def get_value_with_suffix(value): if abs(value) >= 10 ** 9: diff --git a/src/urh/signalprocessing/ProtocolAnalyzerContainer.py b/src/urh/signalprocessing/ProtocolAnalyzerContainer.py index 92836f16..b8044b4c 100644 --- a/src/urh/signalprocessing/ProtocolAnalyzerContainer.py +++ b/src/urh/signalprocessing/ProtocolAnalyzerContainer.py @@ -94,7 +94,7 @@ class ProtocolAnalyzerContainer(ProtocolAnalyzer): l.refblock += proto_analyzer.num_blocks for p in proto_labels: - self.__group.add_label(p) + self.__group.add_label(p, decode=False) for block in self.blocks: block.fuzz_labels[index:0] = [p for p in proto_labels if p not in block.fuzz_labels] @@ -114,7 +114,7 @@ class ProtocolAnalyzerContainer(ProtocolAnalyzer): self.qt_signals.line_duplicated.emit() def refresh_protolabel_blocks(self): - self.__group.refresh_labels() + self.__group.refresh_labels(decode=False) def fuzz_successive(self): """ diff --git a/src/urh/signalprocessing/ProtocolGroup.py b/src/urh/signalprocessing/ProtocolGroup.py index 27986117..6f88dd5b 100644 --- a/src/urh/signalprocessing/ProtocolGroup.py +++ b/src/urh/signalprocessing/ProtocolGroup.py @@ -110,17 +110,20 @@ class ProtocolGroup(object): except IndexError: return None - def refresh_label(self, lbl: ProtocolLabel): + def refresh_label(self, lbl: ProtocolLabel, decode=True): if lbl not in self.labels: print("Label {0} is not in Group {1}".format(lbl.name, self.name), file=sys.stderr) return - lbl.find_block_numbers(self.decoded_bits_str) + if decode: + lbl.find_block_numbers(self.decoded_bits_str) + else: + lbl.find_block_numbers(self.plain_bits_str) - def refresh_labels(self): + def refresh_labels(self, decode=True): for lbl in self.labels: #lbl.signals.apply_decoding_changed.emit(lbl) # Update DnD Labels for new blocks - self.refresh_label(lbl) + self.refresh_label(lbl, decode) def convert_index(self, index: int, from_view: int, to_view: int, decoded: bool, block_indx=-1) -> tuple: """ @@ -224,12 +227,12 @@ class ProtocolGroup(object): return proto_label - def add_label(self, lbl: ProtocolLabel, refresh=True): + def add_label(self, lbl: ProtocolLabel, refresh=True, decode=True): if lbl not in self.labels: lbl.signals.apply_decoding_changed.connect(self.handle_plabel_apply_decoding_changed) self.labels.append(lbl) if refresh: - self.refresh_label(lbl) + self.refresh_label(lbl, decode=decode) self.labels.sort() def handle_plabel_apply_decoding_changed(self, lbl: ProtocolLabel): diff --git a/src/urh/ui/GridScene.py b/src/urh/ui/GridScene.py index 4ccfcf97..8742d902 100644 --- a/src/urh/ui/GridScene.py +++ b/src/urh/ui/GridScene.py @@ -46,13 +46,20 @@ class GridScene(ZoomableScene): painter.scale(scale_x, scale_y) font_height = self.font_metrics.height() + counter = -1 # Counter for Label for every second line for x in x_range: freq = self.frequencies[x] + counter += 1 + + if freq != 0 and (counter % 2 != 0): # Label for every second line + continue + if freq != 0: prefix = "+" if freq > 0 else "" - value = prefix+Formatter.big_value_with_suffix(freq) + value = prefix+Formatter.big_value_with_suffix(freq, 2) else: + counter = 0 value = Formatter.big_value_with_suffix(self.center_freq) font_width = self.font_metrics.width(value) painter.drawText(x / scale_x - font_width / 2, diff --git a/src/urh/ui/KillerDoubleSpinBox.py b/src/urh/ui/KillerDoubleSpinBox.py index 81d4b563..3db53b79 100644 --- a/src/urh/ui/KillerDoubleSpinBox.py +++ b/src/urh/ui/KillerDoubleSpinBox.py @@ -22,12 +22,10 @@ class KillerDoubleSpinBox(QDoubleSpinBox): if self.suffix() != text[-1]: if self.auto_suffix: self.setSuffix(text[-1]) - self.on_text_edited() else: if self.suffix() != "": if self.auto_suffix: self.setSuffix("") - self.on_text_edited() def on_text_edited(self): self.lineEdit().setText(self.lineEdit().text().upper()) @@ -39,7 +37,6 @@ class KillerDoubleSpinBox(QDoubleSpinBox): def setUnit(self): value = abs(self.value()) - if 10 ** 9 <= value <= 10 ** 11: if self.suffix() != "G" and self.auto_suffix: self.setSuffix("G") @@ -85,6 +82,6 @@ class KillerDoubleSpinBox(QDoubleSpinBox): return super().valueFromText(text) def validate(self, inpt: str, pos: int): - rx = QRegExp("^(-?[0-9]+)[.]?[0-9]*[kKmMgG]?$") + rx = QRegExp("^(-?[0-9]+)[.]?[0-9]*[kKmMgG"+str(self.suffix())+"]?$") result = QValidator.Acceptable if rx.exactMatch(inpt.replace(",", ".")) else QValidator.Invalid return result, inpt, pos diff --git a/src/urh/ui/views/EpicGraphicView.py b/src/urh/ui/views/EpicGraphicView.py index bb8524b0..7ffc56c7 100644 --- a/src/urh/ui/views/EpicGraphicView.py +++ b/src/urh/ui/views/EpicGraphicView.py @@ -223,6 +223,7 @@ class EpicGraphicView(SelectableGraphicView): self.resetTransform() x_factor = self.width() / self.sceneRect().width() self.scale(x_factor, y_factor) + self.centerOn(0, self.y_center) def zoom_to_selection(self, start: int, end: int): if start == end: @@ -249,4 +250,12 @@ class EpicGraphicView(SelectableGraphicView): self.zoom_all_signals(0.9) def clear_selection(self): - self.set_selection_area(0, 0) \ No newline at end of file + self.set_selection_area(0, 0) + + + def resizeEvent(self, event): + if self.view_rect().width() > self.sceneRect().width(): + x_factor = self.width() / self.sceneRect().width() + self.scale(x_factor / self.transform().m11(), 1) + + self.autofit_view() \ No newline at end of file diff --git a/src/urh/ui/views/LiveGraphicView.py b/src/urh/ui/views/LiveGraphicView.py index 2d242753..4b9a9447 100644 --- a/src/urh/ui/views/LiveGraphicView.py +++ b/src/urh/ui/views/LiveGraphicView.py @@ -42,13 +42,10 @@ class LiveGraphicView(QGraphicsView): self.zoomed.emit(zoom_factor) def mouseMoveEvent(self, event: QMouseEvent): - try: - if isinstance(self.scene(), GridScene): - freq = self.scene().get_freq_for_pos(int(self.mapToScene(event.pos()).x())) - if freq is not None: - QToolTip.showText(self.mapToGlobal(event.pos()), Formatter.big_value_with_suffix(freq), None, QRect(), 10000) - except TypeError: - pass # Frequency not ready yet + if isinstance(self.scene(), GridScene): + freq = self.scene().get_freq_for_pos(int(self.mapToScene(event.pos()).x())) + if freq is not None: + QToolTip.showText(self.mapToGlobal(event.pos()), "Tune to:"+Formatter.big_value_with_suffix(freq), None, QRect(), 10000) def mousePressEvent(self, event: QMouseEvent): if isinstance(self.scene(), GridScene): diff --git a/src/urh/util/Formatter.py b/src/urh/util/Formatter.py index 12c2ea27..92416cf3 100644 --- a/src/urh/util/Formatter.py +++ b/src/urh/util/Formatter.py @@ -22,12 +22,13 @@ class Formatter(): return locale.format_string("%.2f " + suffix, value) + "s" @staticmethod - def big_value_with_suffix(value: float) -> str: + def big_value_with_suffix(value: float, decimals=3) -> str: + fmt_str = "%.{0:d}f".format(decimals) if abs(value) >= 1e9: - return locale.format_string("%.3fG", value / 1e9) + return locale.format_string(fmt_str+"G", value / 1e9) elif abs(value) >= 1e6: - return locale.format_string("%.3fM", value / 1e6) + return locale.format_string(fmt_str+"M", value / 1e6) elif abs(value) >= 1e3: - return locale.format_string("%.3fK", value / 1e3) + return locale.format_string(fmt_str+"K", value / 1e3) else: - return locale.format_string("%.3f", value) + return locale.format_string(fmt_str, value) diff --git a/tests/GFSK.py b/tests/GFSK.py new file mode 100644 index 00000000..516fd82e --- /dev/null +++ b/tests/GFSK.py @@ -0,0 +1,60 @@ +import copy +import unittest + +import matplotlib.pyplot as plt +import numpy as np + +from urh.signalprocessing.Modulator import Modulator +from urh.signalprocessing.ProtocolAnalyzer import ProtocolAnalyzer +from urh.signalprocessing.Signal import Signal +from urh.cythonext import signalFunctions + +class GFSK(unittest.TestCase): + def test_plot(self): + modulator = Modulator("gfsk") + modulator.modulation_type_str = "GFSK" + modulator.samples_per_bit = 100 + modulator.sample_rate = 1e6 + modulator.param_for_one = 20e3 + modulator.param_for_zero = 10e3 + modulator.carrier_freq_hz = 15e3 + modulator.carrier_phase_deg = 90 + + + modulator.modulate([True, False, True, False, False], 77) + data = copy.deepcopy(modulator.modulated_samples) + modulator.modulate([False, True, True, True, True, False, True], 100, start=len(data)) + data = np.concatenate((data, modulator.modulated_samples)) + + plt.subplot(2, 1, 1) + axes = plt.gca() + axes.set_ylim([-2,2]) + plt.plot(data.real) + plt.title("Modulated Wave") + + plt.subplot(2, 1, 2) + qad = signalFunctions.afp_demod(np.ascontiguousarray(data), 0, 1) + plt.plot(qad) + plt.title("Quad Demod") + + plt.show() + + + def test_gfsk(self): + modulator = Modulator("gfsk") + modulator.modulation_type_str = "FSK" + modulator.samples_per_bit = 100 + modulator.sample_rate = 1e6 + modulator.param_for_one = 20e3 + modulator.param_for_zero = -10e3 + modulator.modulate([True, False, False, True, False], 9437) + s = modulator.modulated_samples + modulator.modulate([True, False, True], 9845) #, start=len(s)) + s = np.concatenate((s, modulator.modulated_samples)) + modulator.modulate([True, False, True, False], 8457) #, start=len(s)) + s = np.concatenate((s, modulator.modulated_samples)) + + s.tofile("/tmp/test.complex") + + pa = ProtocolAnalyzer(Signal("/tmp/test.complex", "test", modulation="FSK")) + pa.get_protocol_from_signal() \ No newline at end of file diff --git a/tests/TestLoadProtocolFile.py b/tests/TestLoadProtocolFile.py deleted file mode 100644 index 3c0af747..00000000 --- a/tests/TestLoadProtocolFile.py +++ /dev/null @@ -1,26 +0,0 @@ -import os -import unittest -from urh import constants - -from urh.signalprocessing.ProtocolAnalyzer import ProtocolAnalyzer -from urh.signalprocessing.ProtocolBlock import ProtocolBlock -from urh.signalprocessing.encoding import encoding - - -class TestLoadProtocolFile(unittest.TestCase): - # Testmethode muss immer mit Präfix test_* starten - def test_proto_block_from_str(self): - bits = "1011AB11" - pb = ProtocolBlock.from_plain_bits_str(bits) - self.assertEqual(bits, pb.plain_bits_str) - - # def test_proto_analyzer_from_file(self): - # # TODO Compareframe methode nutzen, auch testen ob Labels da sind - # self.assertTrue(os.path.isfile("./data/protocol.txt")) - # pa, view_type, encoding = ProtocolAnalyzer.from_file("./data/protocol.txt") - # self.assertEqual(pa.blocks[0].plain_bits_str, - # "10101010110100111011010111011101110111011100110001011101010001011101110110110101101") - - -if __name__ == "__main__": - unittest.main() \ No newline at end of file diff --git a/ui/modulation.ui b/ui/modulation.ui index dfd6e110..5d2d34fa 100644 --- a/ui/modulation.ui +++ b/ui/modulation.ui @@ -342,180 +342,6 @@ - - - - - - - 0 - 0 - - - - 3 - - - 0.001000000000000 - - - 999999999.990000009536743 - - - - - - - - 0 - 0 - - - - Amplitude for 1: - - - - - - - - 0 - 0 - - - - Amplitude for 0: - - - - - - - - 0 - 0 - - - - Lock SIV to original signal - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - 0 - 0 - - - - - 0 - 0 - - - - - 16777215 - 16777215 - - - - - Amplitude Shift Keying (ASK) - - - - - Frequency Shift Keying (FSK) - - - - - Phase Shift Keying (PSK) - - - - - - - - - 0 - 0 - - - - 3 - - - 0.001000000000000 - - - 999999999.990000009536743 - - - - - - - - 0 - 0 - - - - Samples in View: - - - - - - - - 0 - 0 - - - - <html><head/><body><p>Shown Samples in View:</p><p><span style=" font-weight:600; color:#ff0000;">Red</span> - if samples in view differ from original signal</p><p><span style=" font-weight:600;">Normal</span> - if samples in view are equal to the original signal</p></body></html> - - - 101010121 - - - - - - - Samples selected: - - - - - - - 0 - - - - - @@ -926,6 +752,228 @@ + + + + + + 0.010000000000000 + + + 0.990000000000000 + + + 0.010000000000000 + + + + + + + + 0 + 0 + + + + 3 + + + 0.001000000000000 + + + 999999999.990000009536743 + + + + + + + + 0 + 0 + + + + Amplitude for 1: + + + + + + + + 0 + 0 + + + + Amplitude for 0: + + + + + + + + 0 + 0 + + + + Lock SIV to original signal + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + + Amplitude Shift Keying (ASK) + + + + + Frequency Shift Keying (FSK) + + + + + Gaussian Frequency Shift Keying (GFSK) + + + + + Phase Shift Keying (PSK) + + + + + + + + + 0 + 0 + + + + 3 + + + 0.001000000000000 + + + 999999999.990000009536743 + + + + + + + + 0 + 0 + + + + Samples in View: + + + + + + + + 0 + 0 + + + + <html><head/><body><p>Shown Samples in View:</p><p><span style=" font-weight:600; color:#ff0000;">Red</span> - if samples in view differ from original signal</p><p><span style=" font-weight:600;">Normal</span> - if samples in view are equal to the original signal</p></body></html> + + + 101010121 + + + + + + + Samples selected: + + + + + + + 0 + + + + + + + Gauss BT: + + + + + + + Gauss filter width: + + + + + + + 0.010000000000000 + + + 100.000000000000000 + + + 0.010000000000000 + + + 1.000000000000000 + + + + + diff --git a/ui/send_recv.ui b/ui/send_recv.ui index ce3cbb97..a5067854 100644 --- a/ui/send_recv.ui +++ b/ui/send_recv.ui @@ -501,7 +501,7 @@ Qt::ScrollBarAlwaysOff - Qt::ScrollBarAlwaysOn + Qt::ScrollBarAsNeeded @@ -544,8 +544,19 @@ cbDevice + lineEditIP + spinBoxFreq + spinBoxSampleRate + spinBoxBandwidth spinBoxGain + spinBoxNRepeat + btnStart + btnStop + btnSave + btnClear graphicsView + sliderYscale + txtEditErrors diff --git a/ui/signal_frame.ui b/ui/signal_frame.ui index 537955fa..3123cea7 100644 --- a/ui/signal_frame.ui +++ b/ui/signal_frame.ui @@ -67,26 +67,16 @@ QLayout::SetFixedSize - - - - Replay signal... - - - - .. - - - - - + + - Show the extracted protocol based on the parameters InfoLen, PauseLen and ZeroTreshold (in QuadratureDemod-View). - -If you want your protocol to be better seperated, edit the PauseLen using right-click menu from a selection in SignalView or ProtocolView. + If this is set to true, your selected protocol bits will show up in the signal view, and vice versa. - Show Signal as + Sync Selection + + + true @@ -106,18 +96,17 @@ If you want your protocol to be better seperated, edit the PauseLen using right- - - - - Qt::Vertical + + + + Show the extracted protocol based on the parameters InfoLen, PauseLen and ZeroTreshold (in QuadratureDemod-View). + +If you want your protocol to be better seperated, edit the PauseLen using right-click menu from a selection in SignalView or ProtocolView. - - - 20 - 40 - + + Show Signal as - + @@ -201,6 +190,28 @@ If you want your protocol to be better seperated, edit the PauseLen using right- + + + + + 100 + 0 + + + + + 16777215 + 16777215 + + + + <html><head/><body><p>This is the error tolerance for determining the <span style=" font-weight:600;">pulse lengths</span> in the demodulated signal.</p><p><span style=" text-decoration: underline;">Example:</span> Say, we are reading a ones pulse and the tolerance value was set to 5. Then 5 errors (which must follow sequentially) are accepted.</p><p>Tune this value if you have <span style=" font-weight:600;">spiky data</span> after demodulation.</p></body></html> + + + 9999 + + + @@ -229,7 +240,7 @@ If you want your protocol to be better seperated, edit the PauseLen using right- - + @@ -239,7 +250,7 @@ If you want your protocol to be better seperated, edit the PauseLen using right- - true + false @@ -247,29 +258,7 @@ If you want your protocol to be better seperated, edit the PauseLen using right- - - - - - 100 - 0 - - - - - 16777215 - 16777215 - - - - <html><head/><body><p>This is the error tolerance for determining the <span style=" font-weight:600;">pulse lengths</span> in the demodulated signal.</p><p><span style=" text-decoration: underline;">Example:</span> Say, we are reading a ones pulse and the tolerance value was set to 5. Then 5 errors (which must follow sequentially) are accepted.</p><p>Tune this value if you have <span style=" font-weight:600;">spiky data</span> after demodulation.</p></body></html> - - - 9999 - - - - + @@ -288,22 +277,9 @@ If you want your protocol to be better seperated, edit the PauseLen using right- - - - - If this is set to true, your selected protocol bits will show up in the signal view, and vice versa. - - - Sync Selection - - - true - - - - + @@ -374,7 +350,7 @@ If you want your protocol to be better seperated, edit the PauseLen using right- - + @@ -441,7 +417,7 @@ If you want your protocol to be better seperated, edit the PauseLen using right- - + @@ -464,6 +440,32 @@ If you want your protocol to be better seperated, edit the PauseLen using right- + + + + + 24 + 24 + + + + + 24 + 24 + + + + Replay signal + + + + + + + .. + + + @@ -528,7 +530,7 @@ If you want your protocol to be better seperated, edit the PauseLen using right- - + @@ -566,13 +568,67 @@ If you want your protocol to be better seperated, edit the PauseLen using right- - + + + + Modulation: + + + + + + + FSK + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::Horizontal + + + + + + + Qt::Horizontal + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + <html><head/><body><p>Automatically detect Center and Bit Length, when you change the demodulation type. You can disable this behaviour for faster switching between demodulations.</p></body></html> - Auto-Detect + Autodetect parameters @@ -592,20 +648,6 @@ If you want your protocol to be better seperated, edit the PauseLen using right- - - - - Modulation: - - - - - - - FSK - - - @@ -1086,10 +1128,13 @@ If you want your protocol to be better seperated, edit the PauseLen using right- - btnMinimize btnSaveSignal + btnInfo + btnMinimize btnCloseSignal lineEditSignalName + spinBoxNoiseTreshold + spinBoxCenterOffset spinBoxInfoLen spinBoxTolerance cbSignalView @@ -1103,6 +1148,7 @@ If you want your protocol to be better seperated, edit the PauseLen using right- btnShowHideStartEnd spinBoxSelectionStart spinBoxSelectionEnd + spinBoxXZoom