diff --git a/data/ui/signal_frame.ui b/data/ui/signal_frame.ui index a8c39f15f4..c4af1b7cb1 100644 --- a/data/ui/signal_frame.ui +++ b/data/ui/signal_frame.ui @@ -778,43 +778,6 @@ stop:1 rgba(255, 255, 255, 0)); 0 - - - - - 0 - 150 - - - - - 30 - 16777215 - - - - QFrame::NoFrame - - - Qt::ScrollBarAlwaysOff - - - Qt::ScrollBarAlwaysOn - - - false - - - QGraphicsView::AnchorViewCenter - - - Qt::ContainsItemShape - - - QGraphicsView::DontSavePainterState - - - @@ -1308,11 +1271,6 @@ stop:1 rgba(255, 255, 255, 0)); QGraphicsView
urh.ui.views.EpicGraphicView.h
- - LegendGraphicView - QGraphicsView -
urh.ui.views.LegendGraphicView.h
-
TextEditProtocolView QTextEdit diff --git a/src/urh/ainterpretation/AutoInterpretation.py b/src/urh/ainterpretation/AutoInterpretation.py index 4416100fbb..dd45db2c32 100644 --- a/src/urh/ainterpretation/AutoInterpretation.py +++ b/src/urh/ainterpretation/AutoInterpretation.py @@ -370,11 +370,11 @@ def estimate(iq_array: IQArray, noise: float = None, modulation: str = None) -> message_indices = merge_message_segments_for_ook(message_indices) if modulation == "OOK" or modulation == "ASK": - data = signal_functions.afp_demod(iq_array.data, noise, 0) + data = signal_functions.afp_demod(iq_array.data, noise, "ASK") elif modulation == "FSK": - data = signal_functions.afp_demod(iq_array.data, noise, 1) + data = signal_functions.afp_demod(iq_array.data, noise, "FSK") elif modulation == "PSK": - data = signal_functions.afp_demod(iq_array.data, noise, 2) + data = signal_functions.afp_demod(iq_array.data, noise, "PSK") else: raise ValueError("Unsupported Modulation") diff --git a/src/urh/ainterpretation/Wavelet.py b/src/urh/ainterpretation/Wavelet.py index a114678570..d22e3feebe 100644 --- a/src/urh/ainterpretation/Wavelet.py +++ b/src/urh/ainterpretation/Wavelet.py @@ -59,7 +59,7 @@ def cwt_haar(x: np.ndarray, scale=10): # data = np.fromfile("/tmp/psk.complex", dtype=np.complex64) # data = np.fromfile("/home/joe/GIT/urh/tests/data/psk_generated.complex", dtype=np.complex64)[0:8000] modulator = Modulator("") - modulator.modulation_type_str = "PSK" + modulator.modulation_type = "PSK" modulator.param_for_zero = 0 modulator.param_for_one = 180 modulator.carrier_freq_hz = 5e3 diff --git a/src/urh/cli/urh_cli.py b/src/urh/cli/urh_cli.py index 6c8f941fd9..248bbe48a3 100755 --- a/src/urh/cli/urh_cli.py +++ b/src/urh/cli/urh_cli.py @@ -106,7 +106,7 @@ def build_modulator_from_args(arguments: argparse.Namespace): result.param_for_zero = param_zero result.param_for_one = param_one - result.modulation_type_str = arguments.modulation_type + result.modulation_type = arguments.modulation_type result.sample_rate = arguments.sample_rate return result @@ -150,7 +150,7 @@ def build_protocol_sniffer_from_args(arguments: argparse.Namespace): bh = build_backend_handler_from_args(arguments) result = ProtocolSniffer(arguments.bit_length, arguments.center, arguments.noise, arguments.tolerance, - Modulator.MODULATION_TYPES.index(arguments.modulation_type), + arguments.modulation_type, arguments.device.lower(), bh) result.rcv_device.frequency = arguments.frequency result.rcv_device.sample_rate = arguments.sample_rate @@ -261,7 +261,7 @@ def parse_project_file(file_path: str): result["carrier_phase"] = modulator.carrier_phase_deg result["parameter_zero"] = modulator.param_for_zero result["parameter_one"] = modulator.param_for_one - result["modulation_type"] = modulator.modulation_type_str + result["modulation_type"] = modulator.modulation_type return result diff --git a/src/urh/constants.py b/src/urh/constants.py index 3099939be1..8fc4485b06 100644 --- a/src/urh/constants.py +++ b/src/urh/constants.py @@ -45,14 +45,14 @@ class color: # ROI-SELECTION COLORS SELECTION_COLOR = QColor("darkblue") # overwritten by system color (bin/urh) NOISE_COLOR = QColor("red") -SELECTION_OPACITY = 0.8 +SELECTION_OPACITY = 1 NOISE_OPACITY = 0.4 # SEPARATION COLORS -ONES_AREA_COLOR = QColor.fromRgb(0, 128, 128) -ZEROS_AREA_COLOR = QColor.fromRgb(90, 9, 148) -SEPARATION_OPACITY = 0.2 -SEPARATION_PADDING = .05 # Prozent +ONES_AREA_COLOR = Qt.darkGreen +ZEROS_AREA_COLOR = Qt.darkRed +SEPARATION_OPACITY = 0.25 +SEPARATION_PADDING = .05 # percent # PROTOCOL TABLE COLORS SELECTED_ROW_COLOR = QColor.fromRgb(0, 0, 255) diff --git a/src/urh/controller/CompareFrameController.py b/src/urh/controller/CompareFrameController.py index 5cab85ede7..252962fa65 100644 --- a/src/urh/controller/CompareFrameController.py +++ b/src/urh/controller/CompareFrameController.py @@ -1046,10 +1046,7 @@ def on_btn_analyze_clicked(self): self.message_type_table_model.update() self.ui.tblViewMessageTypes.clearSelection() except Exception as e: - logger.exception(e) - Errors.generic_error("Failed to assign labels", - "An error occurred during automatic label assignment", - traceback.format_exc()) + Errors.exception(e) self.ui.progressBarLogicAnalyzer.setValue(90) diff --git a/src/urh/controller/GeneratorTabController.py b/src/urh/controller/GeneratorTabController.py index 47ff6736e6..17e55f9b1c 100644 --- a/src/urh/controller/GeneratorTabController.py +++ b/src/urh/controller/GeneratorTabController.py @@ -231,7 +231,7 @@ def show_modulation_info(self): 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 + mod_type = cur_mod.modulation_type self.ui.lModTypeValue.setText(mod_type) if mod_type == "ASK": prefix = "Amplitude" @@ -394,7 +394,7 @@ def generate_file(self): sample_rate = 1e6 FileOperator.save_data_dialog("generated", 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()) + Errors.exception(e) self.unsetCursor() def prepare_modulation_buffer(self, total_samples: int, show_error=True) -> IQArray: @@ -606,7 +606,7 @@ def on_btn_send_clicked(self): 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()) + Errors.exception(e) self.unsetCursor() @pyqtSlot() diff --git a/src/urh/controller/MainController.py b/src/urh/controller/MainController.py index 46ac56f9c3..e62e83a50e 100644 --- a/src/urh/controller/MainController.py +++ b/src/urh/controller/MainController.py @@ -352,7 +352,7 @@ def close_signal_frame(self, signal_frame: SignalFrame): self.set_frame_numbers() self.refresh_main_menu() except Exception as e: - Errors.generic_error(self.tr("Failed to close"), str(e), traceback.format_exc()) + Errors.exception(e) self.unsetCursor() def add_files(self, filepaths, group_id=0, enforce_sample_rate=None): @@ -560,7 +560,7 @@ def on_open_recent_action_triggered(self): self.add_files(FileOperator.uncompress_archives([action.data()], QDir.tempPath())) self.unsetCursor() except Exception as e: - Errors.generic_error(self.tr("Failed to open"), str(e), traceback.format_exc()) + Errors.exception(e) self.unsetCursor() @pyqtSlot() @@ -783,7 +783,7 @@ def show_open_dialog(self, directory=False): self.add_files(file_names) self.unsetCursor() except Exception as e: - Errors.generic_error(self.tr("Failed to open"), str(e), traceback.format_exc()) + Errors.exception(e) self.unsetCursor() @pyqtSlot() diff --git a/src/urh/controller/SimulatorTabController.py b/src/urh/controller/SimulatorTabController.py index c8957dae5f..2eb829cabe 100644 --- a/src/urh/controller/SimulatorTabController.py +++ b/src/urh/controller/SimulatorTabController.py @@ -442,7 +442,7 @@ def on_btn_simulate_clicked(self): try: self.get_simulator_dialog().exec_() except Exception as e: - Errors.generic_error("An error occurred", str(e)) + Errors.exception(e) def get_simulator_dialog(self) -> SimulatorDialog: protos = [p for proto_list in self.tree_model.protocols.values() for p in proto_list] diff --git a/src/urh/controller/dialogs/ModulatorDialog.py b/src/urh/controller/dialogs/ModulatorDialog.py index c0795265ca..35ef802673 100644 --- a/src/urh/controller/dialogs/ModulatorDialog.py +++ b/src/urh/controller/dialogs/ModulatorDialog.py @@ -121,7 +121,7 @@ def current_modulator(self): return mod def set_ui_for_current_modulator(self): - index = self.ui.comboBoxModulationType.findText("*(" + self.current_modulator.modulation_type_str + ")", + index = self.ui.comboBoxModulationType.findText("*(" + self.current_modulator.modulation_type + ")", Qt.MatchWildcard) self.ui.comboBoxModulationType.setCurrentIndex(index) self.ui.doubleSpinBoxCarrierFreq.setValue(self.current_modulator.carrier_freq_hz) @@ -443,10 +443,10 @@ def on_gaus_filter_wdith_changed(self): @pyqtSlot() def on_modulation_type_changed(self): - if self.current_modulator.modulation_type_str == self.__cur_selected_mod_type(): + if self.current_modulator.modulation_type == self.__cur_selected_mod_type(): write_standard_parameters = False else: - self.current_modulator.modulation_type_str = self.__cur_selected_mod_type() + self.current_modulator.modulation_type = self.__cur_selected_mod_type() write_standard_parameters = True self.__set_gauss_ui_visibility(self.__cur_selected_mod_type() == "GFSK") diff --git a/src/urh/controller/dialogs/SimulatorDialog.py b/src/urh/controller/dialogs/SimulatorDialog.py index cb8a2dfbd2..bce8d9bde7 100644 --- a/src/urh/controller/dialogs/SimulatorDialog.py +++ b/src/urh/controller/dialogs/SimulatorDialog.py @@ -367,7 +367,7 @@ def on_selected_tx_device_changed(self): self.device_settings_tx_widget.device = self.simulator.sender.device except Exception as e: self.device_settings_tx_widget.ui.cbDevice.setCurrentText(old_name) - Errors.generic_error("Error occurred", str(e)) + Errors.exception(e) @pyqtSlot() def on_btn_test_sniff_settings_clicked(self): diff --git a/src/urh/controller/widgets/ModulationSettingsWidget.py b/src/urh/controller/widgets/ModulationSettingsWidget.py index ab1c77fe70..b2e614d171 100644 --- a/src/urh/controller/widgets/ModulationSettingsWidget.py +++ b/src/urh/controller/widgets/ModulationSettingsWidget.py @@ -49,10 +49,10 @@ def show_selected_modulation_infos(self): self.ui.labelCarrierFrequencyValue.setText(modulator.carrier_frequency_str) self.ui.labelBitLengthValue.setText(modulator.bit_len_str) self.ui.labelSampleRateValue.setText(modulator.sample_rate_str) - self.ui.labelModulationTypeValue.setText(modulator.modulation_type_verbose_str) + self.ui.labelModulationTypeValue.setText(modulator.modulation_type_verbose) prefixes = {"ASK": "Amplitude", "PSK": "Phase", "FSK": "Frequency", "GFSK": "Frequency"} - self.ui.labelParamZero.setText(prefixes[modulator.modulation_type_str] + " for 0:") - self.ui.labelParamOne.setText(prefixes[modulator.modulation_type_str] + " for 1:") + self.ui.labelParamZero.setText(prefixes[modulator.modulation_type] + " for 0:") + self.ui.labelParamOne.setText(prefixes[modulator.modulation_type] + " for 1:") self.ui.labelParamZeroValue.setText(modulator.param_for_zero_str) self.ui.labelParamOneValue.setText(modulator.param_for_one_str) diff --git a/src/urh/controller/widgets/SignalFrame.py b/src/urh/controller/widgets/SignalFrame.py index 29354aebdd..ff697c922a 100644 --- a/src/urh/controller/widgets/SignalFrame.py +++ b/src/urh/controller/widgets/SignalFrame.py @@ -121,7 +121,6 @@ def __init__(self, proto_analyzer: ProtocolAnalyzer, undo_stack: QUndoStack, pro else: self.ui.lSignalTyp.setText("Complex Signal") - self.ui.gvLegend.hide() self.ui.lineEditSignalName.setText(self.signal.name) self.ui.lSamplesInView.setText("{0:,}".format(self.signal.num_samples)) self.ui.lSamplesTotal.setText("{0:,}".format(self.signal.num_samples)) @@ -134,6 +133,7 @@ def __init__(self, proto_analyzer: ProtocolAnalyzer, undo_stack: QUndoStack, pro self.scene_manager = SignalSceneManager(self.signal, self) self.ui.gvSignal.scene_manager = self.scene_manager + self.scene_manager.scene.setParent(self.ui.gvSignal) self.ui.gvSignal.setScene(self.scene_manager.scene) self.jump_sync = True @@ -178,7 +178,7 @@ def create_connects(self): self.signal.bit_len_changed.connect(self.ui.spinBoxInfoLen.setValue) self.signal.qad_center_changed.connect(self.on_signal_qad_center_changed) self.signal.noise_threshold_changed.connect(self.on_noise_threshold_changed) - self.signal.modulation_type_changed.connect(self.ui.cbModulationType.setCurrentIndex) + self.signal.modulation_type_changed.connect(self.ui.cbModulationType.setCurrentText) self.signal.tolerance_changed.connect(self.ui.spinBoxTolerance.setValue) self.signal.protocol_needs_update.connect(self.refresh_protocol) self.signal.data_edited.connect(self.on_signal_data_edited) # Crop/Delete Mute etc. @@ -188,7 +188,6 @@ def create_connects(self): self.signal.saved_status_changed.connect(self.on_signal_data_changed_before_save) self.ui.btnSaveSignal.clicked.connect(self.save_signal) self.signal.name_changed.connect(self.ui.lineEditSignalName.setText) - self.ui.gvLegend.resized.connect(self.on_gv_legend_resized) self.ui.gvSignal.selection_width_changed.connect(self.start_proto_selection_timer) self.ui.gvSignal.sel_area_start_end_changed.connect(self.start_proto_selection_timer) @@ -211,7 +210,6 @@ def create_connects(self): self.ui.gvSpectrogram.sel_area_start_end_changed.connect(self.update_selection_area) self.ui.gvSpectrogram.selection_height_changed.connect(self.update_number_selected_samples) self.ui.gvSignal.sep_area_changed.connect(self.set_qad_center) - self.ui.gvSignal.sep_area_moving.connect(self.update_legend) self.ui.sliderYScale.valueChanged.connect(self.on_slider_y_scale_value_changed) self.ui.spinBoxXZoom.valueChanged.connect(self.on_spinbox_x_zoom_value_changed) @@ -223,7 +221,7 @@ def create_connects(self): self.proto_selection_timer.timeout.connect(self.update_number_selected_samples) self.ui.cbSignalView.currentIndexChanged.connect(self.on_cb_signal_view_index_changed) - self.ui.cbModulationType.currentIndexChanged.connect(self.on_combobox_modulation_type_index_changed) + self.ui.cbModulationType.currentTextChanged.connect(self.on_combobox_modulation_type_text_changed) self.ui.cbProtoView.currentIndexChanged.connect(self.on_combo_box_proto_view_index_changed) self.ui.chkBoxShowProtocol.stateChanged.connect(self.set_protocol_visibility) @@ -252,7 +250,7 @@ def refresh_signal_information(self, block=True): self.ui.spinBoxCenterOffset.setValue(self.signal.qad_center) self.ui.spinBoxInfoLen.setValue(self.signal.bit_len) self.ui.spinBoxNoiseTreshold.setValue(self.signal.noise_threshold_relative) - self.ui.cbModulationType.setCurrentIndex(self.signal.modulation_type) + self.ui.cbModulationType.setCurrentText(self.signal.modulation_type) self.ui.btnAdvancedModulationSettings.setVisible(self.ui.cbModulationType.currentText() == "ASK") self.ui.spinBoxTolerance.blockSignals(False) @@ -419,7 +417,7 @@ def save_signal_as(self): try: FileOperator.save_data_dialog(self.signal.name, self.signal.iq_array, self.signal.sample_rate, self.signal.wav_mode) except Exception as e: - Errors.generic_error("Error saving file", str(e), traceback.format_exc()) + Errors.exception(e) def export_demodulated(self): try: @@ -442,9 +440,6 @@ def export_demodulated(self): QMessageBox.critical(self, self.tr("Error exporting demodulated data"), e.args[0]) def draw_signal(self, full_signal=False): - gv_legend = self.ui.gvLegend - gv_legend.y_sep = -self.signal.qad_center - self.scene_manager.scene_type = self.ui.cbSignalView.currentIndex() self.scene_manager.init_scene() if full_signal: @@ -452,13 +447,6 @@ def draw_signal(self, full_signal=False): else: self.ui.gvSignal.redraw_view() - legend = LegendScene() - legend.setBackgroundBrush(constants.BGCOLOR) - legend.setSceneRect(0, self.scene_manager.scene.sceneRect().y(), gv_legend.width(), - self.scene_manager.scene.sceneRect().height()) - legend.draw_one_zero_arrows(-self.signal.qad_center) - gv_legend.setScene(legend) - self.ui.gvSignal.y_sep = -self.signal.qad_center def restore_protocol_selection(self, sel_start, sel_end, start_message, end_message, old_protoview): @@ -595,7 +583,6 @@ def eliminate(self): self.proto_analyzer.eliminate() self.ui.gvSignal.scene_manager.eliminate() - self.ui.gvLegend.eliminate() self.ui.gvSignal.eliminate() self.ui.gvSpectrogram.eliminate() @@ -682,15 +669,6 @@ def on_protocol_updated(self): self.ui.txtEdProto.setEnabled(True) self.ui.txtEdProto.setHtml(self.proto_analyzer.plain_to_html(self.proto_view)) - @pyqtSlot(float) - def update_legend(self, y_sep): - if self.ui.gvLegend.isVisible(): - self.ui.gvLegend.y_sep = y_sep - self.ui.gvLegend.refresh() - self.ui.spinBoxCenterOffset.blockSignals(True) - self.ui.spinBoxCenterOffset.setValue(-y_sep) - self.ui.spinBoxCenterOffset.blockSignals(False) - @pyqtSlot() def handle_protocol_sync_changed(self): self.sync_protocol = self.ui.chkBoxSyncSelection.isChecked() @@ -727,13 +705,6 @@ def on_cb_signal_view_index_changed(self): self.ui.gvSignal.redraw_view(reinitialize=True) self.ui.labelRSSI.show() - if self.ui.cbSignalView.currentIndex() == 1: - self.ui.gvLegend.y_scene = self.scene_manager.scene.sceneRect().y() - self.ui.gvLegend.scene_height = self.scene_manager.scene.sceneRect().height() - self.ui.gvLegend.refresh() - else: - self.ui.gvLegend.hide() - self.ui.gvSignal.auto_fit_view() self.ui.gvSignal.refresh_selection_area() qApp.processEvents() @@ -994,11 +965,9 @@ def refresh_signal(self, draw_full_signal=False): @pyqtSlot(float) def on_signal_qad_center_changed(self, qad_center): self.ui.gvSignal.y_sep = -qad_center - self.ui.gvLegend.y_sep = -qad_center if self.ui.cbSignalView.currentIndex() > 0: self.scene_manager.scene.draw_sep_area(-qad_center) - self.ui.gvLegend.refresh() self.ui.spinBoxCenterOffset.blockSignals(False) self.ui.spinBoxCenterOffset.setValue(qad_center) @@ -1029,7 +998,7 @@ def contextMenuEvent(self, event: QContextMenuEvent): def show_modulation_type(self): self.ui.cbModulationType.blockSignals(True) - self.ui.cbModulationType.setCurrentIndex(self.signal.modulation_type) + self.ui.cbModulationType.setCurrentText(self.signal.modulation_type) self.ui.cbModulationType.blockSignals(False) def on_participant_changed(self): @@ -1050,20 +1019,17 @@ def on_info_btn_clicked(self): sdc = SignalDetailsDialog(self.signal, self) sdc.show() - @pyqtSlot(int) - def on_combobox_modulation_type_index_changed(self, index: int): - if index != self.signal.modulation_type: + @pyqtSlot(str) + def on_combobox_modulation_type_text_changed(self, txt: str): + if txt != self.signal.modulation_type: modulation_action = ChangeSignalParameter(signal=self.signal, protocol=self.proto_analyzer, parameter_name="modulation_type", - parameter_value=index) + parameter_value=txt) self.undo_stack.push(modulation_action) if self.ui.cbSignalView.currentIndex() == 1: self.scene_manager.init_scene() - self.ui.gvLegend.y_scene = self.scene_manager.scene.sceneRect().y() - self.ui.gvLegend.scene_height = self.scene_manager.scene.sceneRect().height() - self.ui.gvLegend.refresh() self.on_slider_y_scale_value_changed() self.ui.btnAdvancedModulationSettings.setVisible(self.ui.cbModulationType.currentText() == "ASK") @@ -1090,13 +1056,6 @@ def on_signal_data_changed_before_save(self): self.ui.lineEditSignalName.setFont(font) - @pyqtSlot() - def on_gv_legend_resized(self): - if self.ui.gvLegend.isVisible(): - self.ui.gvLegend.y_zoom_factor = self.ui.gvSignal.transform().m22() - self.ui.gvLegend.refresh() - self.ui.gvLegend.translate(0, 1) # Resize verschiebt sonst Pfeile - @pyqtSlot() def on_btn_show_hide_start_end_clicked(self): show = self.ui.btnShowHideStartEnd.isChecked() @@ -1263,7 +1222,6 @@ def on_export_fta_wanted(self): filename=filename, include_amplitude=filename.endswith(".fta")) except Exception as e: - logger.exception(e) - Errors.generic_error("Failed to export spectrogram", str(e), traceback.format_exc()) + Errors.exception(e) finally: QApplication.restoreOverrideCursor() diff --git a/src/urh/controller/widgets/SniffSettingsWidget.py b/src/urh/controller/widgets/SniffSettingsWidget.py index aeff74714d..eb75848878 100644 --- a/src/urh/controller/widgets/SniffSettingsWidget.py +++ b/src/urh/controller/widgets/SniffSettingsWidget.py @@ -33,7 +33,7 @@ def __init__(self, device_name: str, project_manager: ProjectManager, signal=Non center=self.ui.spinbox_sniff_Center.value(), noise=self.ui.spinbox_sniff_Noise.value(), tolerance=self.ui.spinbox_sniff_ErrorTolerance.value(), - modulation_type=self.ui.combox_sniff_Modulation.currentIndex(), + modulation_type=self.ui.combox_sniff_Modulation.currentText(), device=device_name, backend_handler=BackendHandler() if backend_handler is None else backend_handler, network_raw_mode=network_raw_mode) @@ -77,7 +77,7 @@ def set_val(widget, key: str, default): set_val(self.ui.spinbox_sniff_Center, "center", signal.qad_center if signal else 0.02) set_val(self.ui.spinbox_sniff_ErrorTolerance, "tolerance", signal.tolerance if signal else 5) set_val(self.ui.spinbox_sniff_Noise, "noise", signal.noise_threshold_relative if signal else 0.001) - set_val(self.ui.combox_sniff_Modulation, "modulation_index", signal.modulation_type if signal else 1) + self.ui.combox_sniff_Modulation.setCurrentText(conf_dict.get("modulation_type", signal.modulation_type if signal else "FSK")) self.ui.comboBox_sniff_encoding.setCurrentText(conf_dict.get("decoding_name", "")) self.ui.checkBoxAdaptiveNoise.setChecked(bool(conf_dict.get("adaptive_noise", False))) self.ui.checkBoxAutoCenter.setChecked(bool(conf_dict.get("automatic_center", False))) @@ -90,7 +90,7 @@ def create_connects(self): self.ui.spinbox_sniff_Center.editingFinished.connect(self.on_center_edited) self.ui.spinbox_sniff_BitLen.editingFinished.connect(self.on_bit_len_edited) self.ui.spinbox_sniff_ErrorTolerance.editingFinished.connect(self.on_tolerance_edited) - self.ui.combox_sniff_Modulation.currentIndexChanged.connect(self.on_modulation_changed) + self.ui.combox_sniff_Modulation.currentTextChanged.connect(self.on_modulation_changed) self.ui.comboBox_sniff_viewtype.currentIndexChanged.connect(self.on_view_type_changed) self.ui.lineEdit_sniff_OutputFile.editingFinished.connect(self.on_line_edit_output_file_editing_finished) self.ui.comboBox_sniff_encoding.currentIndexChanged.connect(self.on_combobox_sniff_encoding_index_changed) @@ -112,7 +112,7 @@ def emit_sniff_parameters_changed(self): center=self.sniffer.signal.qad_center, noise=self.sniffer.signal.noise_threshold, tolerance=self.sniffer.signal.tolerance, - modulation_index=self.sniffer.signal.modulation_type, + modulation_type=self.sniffer.signal.modulation_type, decoding_name=self.sniffer.decoder.name, adaptive_noise=self.sniffer.adaptive_noise, automatic_center=self.sniffer.automatic_center)) @@ -137,9 +137,9 @@ def on_tolerance_edited(self): self.sniffer.signal.tolerance = self.ui.spinbox_sniff_ErrorTolerance.value() self.sniff_setting_edited.emit() - @pyqtSlot(int) - def on_modulation_changed(self, new_index: int): - self.sniffer.signal.silent_set_modulation_type(new_index) + @pyqtSlot(str) + def on_modulation_changed(self, new_modulation: str): + self.sniffer.signal.silent_set_modulation_type(new_modulation) self.sniff_setting_edited.emit() @pyqtSlot() @@ -186,7 +186,7 @@ def on_btn_sniff_use_signal_clicked(self): self.ui.spinbox_sniff_Center.setValue(signal.qad_center) self.ui.spinbox_sniff_Noise.setValue(signal.noise_threshold_relative) self.ui.spinbox_sniff_ErrorTolerance.setValue(signal.tolerance) - self.ui.combox_sniff_Modulation.setCurrentIndex(signal.modulation_type) + self.ui.combox_sniff_Modulation.setCurrentText(signal.modulation_type) self.emit_editing_finished_signals() diff --git a/src/urh/cythonext/signal_functions.pyx b/src/urh/cythonext/signal_functions.pyx index 0ae9d58b85..72ac221427 100644 --- a/src/urh/cythonext/signal_functions.pyx +++ b/src/urh/cythonext/signal_functions.pyx @@ -5,6 +5,7 @@ import numpy as np from libcpp cimport bool from libc.stdint cimport uint8_t, uint16_t, uint32_t, int64_t +from libc.stdio cimport printf from urh.cythonext.util cimport IQ, iq, bit_array_to_number from cython.parallel import prange @@ -19,22 +20,33 @@ cdef extern from "math.h" nogil: # because it can lead to OS X error: https://github.com/jopohl/urh/issues/273 # np.import_array() +cdef int64_t PAUSE_STATE = -1 + cdef float complex imag_unit = 1j cdef float NOISE_FSK_PSK = -4.0 cdef float NOISE_ASK = 0.0 -cpdef float get_noise_for_mod_type(int mod_type): - if mod_type == 0: +cdef float get_noise_for_mod_type(str mod_type): + if mod_type == "ASK": return NOISE_ASK - elif mod_type == 1: + elif mod_type == "FSK": return NOISE_FSK_PSK - elif mod_type == 2: + elif mod_type == "PSK" or mod_type == "OQPSK": return NOISE_FSK_PSK - elif mod_type == 3: # ASK + PSK (QAM) + elif mod_type == "QAM": return NOISE_ASK * NOISE_FSK_PSK else: return 0 +cdef tuple get_value_range_of_mod_type(str mod_type): + if mod_type == "ASK": + return 0, 1 + if mod_type in ("GFSK", "FSK", "OQPSK", "PSK"): + return -np.pi, np.pi + + printf("Warning unknown mod type for value range") + return 0, 0 + cdef get_numpy_dtype(iq cython_type): if str(cython.typeof(cython_type)) == "char": return np.int8 @@ -239,7 +251,7 @@ cdef void costa_demod(IQ samples, float[::1] result, float noise_sqrd, else: result[i] = nco_times_sample.real -cpdef np.ndarray[np.float32_t, ndim=1] afp_demod(IQ samples, float noise_mag, int mod_type): +cpdef np.ndarray[np.float32_t, ndim=1] afp_demod(IQ samples, float noise_mag, str mod_type): if len(samples) <= 2: return np.zeros(len(samples), dtype=np.float32) @@ -285,8 +297,8 @@ cpdef np.ndarray[np.float32_t, ndim=1] afp_demod(IQ samples, float noise_mag, in cdef bool qam = False - if mod_type == 2 or mod_type == 3: # PSK or QAM - if mod_type == 3: + if mod_type in ("PSK", "QAM", "OQPSK"): + if mod_type == "QAM": qam = True costa_alpha = calc_costa_alpha((2 * M_PI / 100)) @@ -302,143 +314,102 @@ cpdef np.ndarray[np.float32_t, ndim=1] afp_demod(IQ samples, float noise_mag, in result[i] = NOISE continue - if mod_type == 0: # ASK + if mod_type == "ASK": result[i] = sqrt(magnitude) / max_magnitude - elif mod_type == 1: # FSK + elif mod_type == "FSK": #tmp = samples[i - 1].conjugate() * c tmp = (samples[i-1, 0] - imag_unit * samples[i-1, 1]) * (real + imag_unit * imag) result[i] = atan2(tmp.imag, tmp.real) # Freq return np.asarray(result) -cpdef unsigned long long find_signal_start(float[::1] demod_samples, int mod_type): - - cdef unsigned long i = 0 - cdef unsigned long ns = len(demod_samples) - cdef unsigned long l = 100 - if ns < 100: - l = ns - - cdef float dsample = 0 - cdef int has_oversteuern = 0 - cdef int conseq_noise = 0 - cdef int conseq_not_noise = 0 - cdef int behind_oversteuern = 0 - cdef float NOISE = get_noise_for_mod_type(mod_type) - - for i in range(0, l): - dsample = demod_samples[i] - if dsample > NOISE: - has_oversteuern = 1 - break - - for i in range(0, ns): - dsample = demod_samples[i] - - if dsample == NOISE: - conseq_noise += 1 - conseq_not_noise = 0 - else: - conseq_noise = 0 - conseq_not_noise += 1 - - if has_oversteuern == 1: - if has_oversteuern and conseq_noise > 100: - behind_oversteuern = 1 +cdef inline int64_t get_current_state(float sample, float[:] thresholds, float noise_val, int n): + if sample == noise_val: + return PAUSE_STATE - if behind_oversteuern and conseq_not_noise == 3: - return i - 3 + cdef int i + for i in range(n): + if sample <= thresholds[i]: + return i - elif conseq_not_noise == 3: - return i -3 + return PAUSE_STATE - return 0 +cpdef int64_t[:, ::1] grab_pulse_lens(float[::1] samples, float center, uint16_t tolerance, + str modulation_type, uint16_t bit_length, + uint8_t bits_per_symbol=1, float center_spacing=0.1): + """ + Get the pulse lengths after quadrature demodulation -cpdef unsigned long long find_signal_end(float[::1] demod_samples, int mod_type): + arr[i][0] gives type of symbol e.g. (arr[i][0] = 1) and (arr[i][0] = 0) for binary modulation + Pause is (arr[i][0] = -1) + arr[i][1] gives length of pulse + """ + cdef bool is_ask = modulation_type == "ASK" + cdef int64_t i, j, pulse_length = 0, num_samples = len(samples) + cdef int64_t cur_index = 0, consecutive_ones = 0, consecutive_zeros = 0, consecutive_pause = 0 + cdef float s = 0, s_prev = 0 + cdef int cur_state = 0, new_state = 0, tmp_state = 0 + cdef float NOISE = get_noise_for_mod_type(modulation_type) - cdef unsigned long long i = 0 - cdef float dsample = 0 - cdef int conseq_not_noise = 0 - cdef float NOISE = get_noise_for_mod_type(mod_type) - cdef unsigned long long ns = len(demod_samples) + cdef int modulation_order = 2**bits_per_symbol - for i in range(ns, 0, -1): - dsample = demod_samples[i] + cdef float[:] thresholds = np.empty(modulation_order, dtype=np.float32) + cdef tuple min_max_of_mod_type = get_value_range_of_mod_type(modulation_type) + cdef float min_of_mod_type = min_max_of_mod_type[0] + cdef float max_of_mod_type = min_max_of_mod_type[1] - if dsample > NOISE: - conseq_not_noise += 1 + cdef int n = modulation_order // 2 - if conseq_not_noise == 3: - return i + 3 + for i in range(0, n): + thresholds[i] = center - (n-(i+1)) * center_spacing - return ns + for i in range(n, modulation_order-1): + thresholds[i] = center + (i+1-n) * center_spacing -cpdef unsigned long long[:, ::1] grab_pulse_lens(float[::1] samples, float center, - unsigned int tolerance, int modulation_type, unsigned int bit_length): - """ - Holt sich die Pulslängen aus den quadraturdemodulierten Samples - - @param samples: Samples nach der QAD - @param center: Alles über der Treshold ist ein Einserpuls, alles darunter 0er Puls - @return: Ein 2D Array arr. - arr[i] gibt Position an. - arr[i][0] gibt an ob Einspuls (arr[i][0] = 1) Nullpuls (arr[i][0] = 0) Pause (arr[i][0] = 42) - arr[i][1] gibt die Länge des Pulses bzw. der Pause an. - """ - cdef int is_ask = modulation_type == 0 - cdef unsigned long long i, pulse_length = 0 - cdef unsigned long long cur_index = 0, consecutive_ones = 0, consecutive_zeros = 0, consecutive_pause = 0 - cdef float s = 0, s_prev = 0 - cdef unsigned short cur_state = 0, new_state = 0 - cdef float NOISE = get_noise_for_mod_type(modulation_type) - cdef unsigned long long num_samples = len(samples) + thresholds[modulation_order-1] = max_of_mod_type - cdef unsigned long long[:, ::1] result = np.zeros((num_samples, 2), dtype=np.uint64, order="C") + cdef int64_t[:, ::1] result = np.zeros((num_samples, 2), dtype=np.int64, order="C") if num_samples == 0: return result + cdef int64_t[:] state_count = np.zeros(modulation_order, dtype=np.int64) + s_prev = samples[0] - if s_prev == NOISE: - cur_state = 42 - elif s_prev > center: - cur_state = 1 - else: - cur_state = 0 + cur_state = get_current_state(s_prev, thresholds, NOISE, modulation_order) for i in range(num_samples): pulse_length += 1 s = samples[i] - if s == NOISE: - consecutive_pause += 1 - consecutive_ones = 0 - consecutive_zeros = 0 - if cur_state == 42: - continue - - elif s > center: - consecutive_ones += 1 - consecutive_zeros = 0 - consecutive_pause = 0 - if cur_state == 1: - continue + tmp_state = get_current_state(s, thresholds, NOISE, modulation_order) + if tmp_state == PAUSE_STATE: + consecutive_pause += 1 else: - consecutive_zeros += 1 - consecutive_ones = 0 consecutive_pause = 0 - if cur_state == 0: - continue - if consecutive_ones > tolerance: - new_state = 1 - elif consecutive_zeros > tolerance: - new_state = 0 - elif consecutive_pause > tolerance: - new_state = 42 + for j in range(0, modulation_order): + if j == tmp_state: + state_count[j] += 1 + else: + state_count[j] = 0 + + if cur_state == tmp_state: + continue + + new_state = -42 + + if consecutive_pause > tolerance: + new_state = PAUSE_STATE else: + for j in range(0, modulation_order): + if state_count[j] > tolerance: + new_state = j + break + + if new_state == -42: continue - if is_ask and cur_state == 42 and (pulse_length - tolerance) < bit_length: + if is_ask and cur_state == PAUSE_STATE and (pulse_length - tolerance) < bit_length: # Aggregate short pauses for ASK cur_state = 0 @@ -453,7 +424,7 @@ cpdef unsigned long long[:, ::1] grab_pulse_lens(float[::1] samples, float cente cur_state = new_state # Append last one - cdef unsigned long long len_result = len(result) + cdef int64_t len_result = len(result) if cur_index < len_result: if cur_index > 0 and result[cur_index - 1, 0] == cur_state: result[cur_index - 1, 1] += pulse_length - tolerance @@ -464,18 +435,6 @@ cpdef unsigned long long[:, ::1] grab_pulse_lens(float[::1] samples, float cente return result[:cur_index] -cpdef unsigned long long estimate_bit_len(float[::1] qad_samples, float qad_center, int tolerance, int mod_type): - - start = find_signal_start(qad_samples, mod_type) - cdef unsigned long long[:, ::1] ppseq = grab_pulse_lens(qad_samples[start:], qad_center, tolerance, mod_type, 0) - cdef unsigned long long i = 0 - cdef unsigned long long l = len(ppseq) - for i in range(0, l): - if ppseq[i, 0] == 1: - return ppseq[i, 1] # first pulse after pause - - return 100 - cpdef int find_nearest_center(float sample, float[::1] centers, int num_centers) nogil: cdef int i = 0 cdef float center = 0 @@ -492,9 +451,6 @@ cpdef int find_nearest_center(float sample, float[::1] centers, int num_centers) return result - -from libc.stdlib cimport malloc, free - cpdef np.ndarray[np.complex64_t, ndim=1] fir_filter(float complex[::1] input_samples, float complex[::1] filter_taps): cdef int i = 0, j = 0 cdef int N = len(input_samples) diff --git a/src/urh/plugins/RfCat/RfCatPlugin.py b/src/urh/plugins/RfCat/RfCatPlugin.py index f36369f44f..4cebd00b02 100644 --- a/src/urh/plugins/RfCat/RfCatPlugin.py +++ b/src/urh/plugins/RfCat/RfCatPlugin.py @@ -167,13 +167,13 @@ def __send_messages(self, messages, sample_rates): if not self.open_rfcat(): return False modulation = self.modulators[messages[0].modulator_index].modulation_type - if modulation == 0: # ASK + if modulation == "ASK": modulation = "MOD_ASK_OOK" - elif modulation == 1: # FSK + elif modulation == "FSK": modulation = "MOD_2FSK" - elif modulation == 2: # GFSK + elif modulation == "GFSK": modulation = "MOD_GFSK" - elif modulation == 3: # PSK + elif modulation == "PSK": modulation = "MOD_MSK" else: # Fallback modulation = "MOD_ASK_OOK" diff --git a/src/urh/signalprocessing/Modulator.py b/src/urh/signalprocessing/Modulator.py index 40530114af..93e22c82e8 100644 --- a/src/urh/signalprocessing/Modulator.py +++ b/src/urh/signalprocessing/Modulator.py @@ -15,14 +15,11 @@ class Modulator(object): - """ - This class can modulate bits to a carrier. - Very useful in generation phase. - """ - MODULATION_TYPES = ["ASK", "FSK", "PSK", "GFSK"] - MODULATION_TYPES_VERBOSE = ["Amplitude Shift Keying (ASK)", "Frequency Shift Keying (FSK)", - "Phase Shift Keying (PSK)", "Gaussian Frequeny Shift Keying (GFSK)"] + MODULATION_TYPES_VERBOSE = {"ASK": "Amplitude Shift Keying (ASK)", + "FSK": "Frequency Shift Keying (FSK)", + "PSK": "Phase Shift Keying (PSK)", + "GFSK": "Gaussian Frequeny Shift Keying (GFSK)"} def __init__(self, name: str): self.carrier_freq_hz = 40 * 10 ** 3 @@ -32,7 +29,7 @@ def __init__(self, name: str): self.samples_per_symbol = 100 self.default_sample_rate = 10 ** 6 self.__sample_rate = None - self.modulation_type = 0 + self.__modulation_type = "ASK" self.bits_per_symbol = 1 @@ -64,6 +61,18 @@ def get_dtype(): else: return np.float32 + @property + def modulation_type(self) -> str: + return self.__modulation_type + + @modulation_type.setter + def modulation_type(self, value): + try: + # legacy support when modulation type was saved as int index + self.__modulation_type = self.MODULATION_TYPES[int(value)] + except (ValueError, IndexError): + self.__modulation_type = value + @property def is_binary_modulation(self): return self.bits_per_symbol == 1 @@ -124,30 +133,18 @@ def sample_rate_str(self): return self.get_value_with_suffix(self.sample_rate) @property - 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 modulation_type_verbose_str(self): + def modulation_type_verbose(self): return self.MODULATION_TYPES_VERBOSE[self.modulation_type] @property def param_for_zero_str(self): - mod = self.MODULATION_TYPES[self.modulation_type] units = {"ASK": "%", "FSK": "Hz", "GFSK": "Hz", "PSK": "°"} - return self.get_value_with_suffix(self.param_for_zero, units[mod]) + return self.get_value_with_suffix(self.param_for_zero, units[self.modulation_type]) @property def param_for_one_str(self): - mod = self.MODULATION_TYPES[self.modulation_type] units = {"ASK": "%", "FSK": "Hz", "GFSK": "Hz", "PSK": "°"} - return self.get_value_with_suffix(self.param_for_one, units[mod]) + return self.get_value_with_suffix(self.param_for_one, units[self.modulation_type]) @property def carrier_data(self): @@ -195,8 +192,6 @@ def modulate(self, data=None, pause=0, start=0) -> IQArray: if len(data) == 0: return IQArray(None, np.float32, 0) - mod_type = self.MODULATION_TYPES[self.modulation_type] - dtype = self.get_dtype() a = self.carrier_amplitude * IQArray.min_max_for_dtype(dtype)[1] @@ -204,13 +199,13 @@ def modulate(self, data=None, pause=0, start=0) -> IQArray: type_val = array.array(type_code, [0])[0] parameters = self.parameters - if mod_type == "ASK": + if self.modulation_type == "ASK": parameters = array.array("f", [a*p/100 for p in parameters]) - elif mod_type == "PSK": + elif self.modulation_type == "PSK": parameters = array.array("f", [p * (math.pi / 180) for p in parameters]) result = signal_functions.modulate_c(data, self.samples_per_symbol, - mod_type, parameters, self.bits_per_symbol, + self.modulation_type, parameters, self.bits_per_symbol, a, self.carrier_freq_hz, self.carrier_phase_deg * (np.pi / 180), self.sample_rate, pause, start, type_val, @@ -221,10 +216,12 @@ def to_xml(self, index: int) -> ET.Element: root = ET.Element("modulator") for attr, val in vars(self).items(): - if attr not in ("data", "_Modulator__sample_rate", "default_sample_rate", "parameters"): + if attr not in ("data", "_Modulator__sample_rate", "_Modulator__modulation_type", + "default_sample_rate", "parameters"): root.set(attr, str(val)) root.set("sample_rate", str(self.__sample_rate)) + root.set("modulation_type", self.__modulation_type) root.set("index", str(index)) root.set("parameters", ",".join(map(str, self.parameters))) @@ -248,10 +245,8 @@ def from_xml(tag: ET.Element): for attrib, value in tag.attrib.items(): if attrib == "index": continue - elif attrib == "name": + elif attrib == "name" or attrib == "modulation_type": setattr(result, attrib, str(value)) - elif attrib == "modulation_type": - setattr(result, attrib, Formatter.str2val(value, int, 0)) elif attrib == "samples_per_bit" or attrib == "samples_per_symbol": # samples_per_bit as legacy support for older project files result.samples_per_symbol = Formatter.str2val(value, int, 100) diff --git a/src/urh/signalprocessing/ProtocolAnalyzer.py b/src/urh/signalprocessing/ProtocolAnalyzer.py index b61740938b..86ec758c3e 100644 --- a/src/urh/signalprocessing/ProtocolAnalyzer.py +++ b/src/urh/signalprocessing/ProtocolAnalyzer.py @@ -9,14 +9,13 @@ from urh import constants from urh.cythonext import signal_functions from urh.signalprocessing.Encoding import Encoding -from urh.signalprocessing.FieldType import FieldType from urh.signalprocessing.Message import Message from urh.signalprocessing.MessageType import MessageType from urh.signalprocessing.Modulator import Modulator from urh.signalprocessing.Participant import Participant from urh.signalprocessing.ProtocoLabel import ProtocolLabel from urh.signalprocessing.Signal import Signal -from urh.util import util as urh_util +from urh.util import util as urh_util, util from urh.util.Logger import logger @@ -226,10 +225,11 @@ def get_protocol_from_signal(self): bit_len = signal.bit_len ppseq = signal_functions.grab_pulse_lens(signal.qad, signal.qad_center, signal.tolerance, - signal.modulation_type, signal.bit_len) + signal.modulation_type, signal.bit_len, signal.bits_per_symbol) - bit_data, pauses, bit_sample_pos = self._ppseq_to_bits(ppseq, bit_len, pause_threshold=signal.pause_threshold) - if signal.message_length_divisor > 1 and signal.modulation_type_str == "ASK": + bit_data, pauses, bit_sample_pos = self._ppseq_to_bits(ppseq, bit_len, self.signal.bits_per_symbol, + pause_threshold=signal.pause_threshold) + if signal.message_length_divisor > 1 and signal.modulation_type == "ASK": self.__ensure_message_length_multiple(bit_data, signal.bit_len, pauses, bit_sample_pos, signal.message_length_divisor) @@ -271,7 +271,7 @@ def __ensure_message_length_multiple(bit_data, bit_len: int, pauses, bit_sample_ bit_sample_pos[i].extend([bit_sample_pos[i][-1] + (k + 1) * bit_len for k in range(missing_bits - 1)]) bit_sample_pos[i].append(bit_sample_pos[i][-1] + pauses[i]) - def _ppseq_to_bits(self, ppseq, bit_len: int, write_bit_sample_pos=True, pause_threshold=8): + def _ppseq_to_bits(self, ppseq, samples_per_symbol: int, bits_per_symbol: int, write_bit_sample_pos=True, pause_threshold=8): bit_sampl_pos = array.array("L", []) bit_sample_positions = [] @@ -281,12 +281,12 @@ def _ppseq_to_bits(self, ppseq, bit_len: int, write_bit_sample_pos=True, pause_t start = 0 total_samples = 0 - pause_type = 42 - zero_pulse_type = 0 - one_pulse_type = 1 + pause_type = -1 there_was_data = False + samples_per_bit = int(samples_per_symbol/bits_per_symbol) + if len(ppseq) > 0 and ppseq[0, 0] == pause_type: start = 1 # Starts with Pause total_samples = ppseq[0, 1] @@ -294,19 +294,20 @@ def _ppseq_to_bits(self, ppseq, bit_len: int, write_bit_sample_pos=True, pause_t for i in range(start, len(ppseq)): cur_pulse_type = ppseq[i, 0] num_samples = ppseq[i, 1] - num_bits_floated = num_samples / bit_len - num_bits = int(num_bits_floated) - decimal_place = num_bits_floated - num_bits + num_symbols_float = num_samples / samples_per_symbol + num_symbols = int(num_symbols_float) + decimal_place = num_symbols_float - num_symbols if decimal_place > 0.5: - num_bits += 1 + num_symbols += 1 if cur_pulse_type == pause_type: # OOK - if num_bits <= pause_threshold or pause_threshold == 0: - data_bits.extend([False] * num_bits) + if num_symbols <= pause_threshold or pause_threshold == 0: + data_bits.extend([0] * (num_symbols * bits_per_symbol)) if write_bit_sample_pos: - bit_sampl_pos.extend([total_samples + k * bit_len for k in range(num_bits)]) + bit_sampl_pos.extend([total_samples + k * samples_per_bit + for k in range(num_symbols*bits_per_symbol)]) elif not there_was_data: # Ignore this pause, if there were no information @@ -325,18 +326,12 @@ def _ppseq_to_bits(self, ppseq, bit_len: int, write_bit_sample_pos=True, pause_t data_bits[:] = array.array("B", []) pauses.append(num_samples) there_was_data = False - - elif cur_pulse_type == zero_pulse_type: - data_bits.extend([False] * num_bits) - if write_bit_sample_pos: - bit_sampl_pos.extend([total_samples + k * bit_len for k in range(num_bits)]) - - elif cur_pulse_type == one_pulse_type: - if not there_was_data: - there_was_data = num_bits > 0 - data_bits.extend([True] * num_bits) + else: + data_bits.extend(util.number_to_bits(cur_pulse_type, bits_per_symbol) * num_symbols) + if not there_was_data and num_symbols > 0: + there_was_data = True if write_bit_sample_pos: - bit_sampl_pos.extend([total_samples + k * bit_len for k in range(num_bits)]) + bit_sampl_pos.extend([total_samples + k * samples_per_bit for k in range(num_symbols*bits_per_symbol)]) total_samples += num_samples diff --git a/src/urh/signalprocessing/ProtocolSniffer.py b/src/urh/signalprocessing/ProtocolSniffer.py index b43f8f0df0..f850cd8d82 100644 --- a/src/urh/signalprocessing/ProtocolSniffer.py +++ b/src/urh/signalprocessing/ProtocolSniffer.py @@ -29,7 +29,7 @@ class ProtocolSniffer(ProtocolAnalyzer, QObject): BUFFER_SIZE_MB = 100 def __init__(self, bit_len: int, center: float, noise: float, tolerance: int, - modulation_type: int, device: str, backend_handler: BackendHandler, network_raw_mode=False): + modulation_type: str, device: str, backend_handler: BackendHandler, network_raw_mode=False): signal = Signal("", "LiveSignal") signal.bit_len = bit_len signal.qad_center = center @@ -204,9 +204,11 @@ def __demodulate_data(self, data): self.signal.qad_center = AutoInterpretation.detect_center(self.signal.qad, max_size=150*self.signal.bit_len) ppseq = grab_pulse_lens(self.signal.qad, self.signal.qad_center, - self.signal.tolerance, self.signal.modulation_type, self.signal.bit_len) + self.signal.tolerance, self.signal.modulation_type, self.signal.bit_len, + self.signal.bits_per_symbol) - bit_data, pauses, bit_sample_pos = self._ppseq_to_bits(ppseq, bit_len, write_bit_sample_pos=False) + bit_data, pauses, bit_sample_pos = self._ppseq_to_bits(ppseq, bit_len, + self.signal.bits_per_symbol, write_bit_sample_pos=False) for bits, pause in zip(bit_data, pauses): message = Message(bits, pause, bit_len=bit_len, message_type=self.default_message_type, diff --git a/src/urh/signalprocessing/Signal.py b/src/urh/signalprocessing/Signal.py index 915a2a2ffb..1ff2a0431c 100644 --- a/src/urh/signalprocessing/Signal.py +++ b/src/urh/signalprocessing/Signal.py @@ -28,7 +28,8 @@ class Signal(QObject): qad_center_changed = pyqtSignal(float) name_changed = pyqtSignal(str) sample_rate_changed = pyqtSignal(float) - modulation_type_changed = pyqtSignal(int) + modulation_type_changed = pyqtSignal(str) + bits_per_symbol_changed = pyqtSignal(int) saved_status_changed = pyqtSignal() protocol_needs_update = pyqtSignal() @@ -53,8 +54,8 @@ def __init__(self, filename: str, name="Signal", modulation: str = None, sample_ self.__changed = False if modulation is None: modulation = "FSK" - self.__modulation_type = self.MODULATION_TYPES.index(modulation) - self.__modulation_order = 2 + self.__modulation_type = modulation + self.__bits_per_symbol = 1 self.__parameter_cache = {mod: {"qad_center": None, "bit_len": None} for mod in self.MODULATION_TYPES} @@ -148,17 +149,11 @@ def parameter_cache(self, val): self.__parameter_cache = val @property - def modulation_type(self): + def modulation_type(self) -> str: return self.__modulation_type @modulation_type.setter - def modulation_type(self, value: int): - """ - 0 - "ASK", 1 - "FSK", 2 - "PSK", 3 - "APSK (QAM)" - - :param value: - :return: - """ + def modulation_type(self, value: str): if self.__modulation_type != value: self.__modulation_type = value self._qad = None @@ -168,12 +163,18 @@ def modulation_type(self, value: int): self.protocol_needs_update.emit() @property - def modulation_type_str(self): - return self.MODULATION_TYPES[self.modulation_type] + def bits_per_symbol(self): + return self.__bits_per_symbol - @modulation_type_str.setter - def modulation_type_str(self, value: str): - self.modulation_type = self.MODULATION_TYPES.index(value) + @bits_per_symbol.setter + def bits_per_symbol(self, value: int): + if self.__bits_per_symbol != value: + self.__bits_per_symbol = value + self._qad = None + + self.bits_per_symbol_changed.emit(self.__bits_per_symbol) + if not self.block_protocol_update: + self.protocol_needs_update.emit() @property def bit_len(self): @@ -312,16 +313,6 @@ def save_as(self, filename: str): self.changed = False QApplication.instance().restoreOverrideCursor() - def get_signal_start(self) -> int: - """ - Index ab dem das Signal losgeht (Nach Übersteuern + Pause am Anfang) - - """ - return signal_functions.find_signal_start(self.qad, self.modulation_type) - - def get_signal_end(self): - return signal_functions.find_signal_end(self.qad, self.modulation_type) - def quad_demod(self): return signal_functions.afp_demod(self.iq_array.data, self.noise_threshold, self.modulation_type) @@ -358,8 +349,8 @@ def create_new(self, start=0, end=0, new_data=None): def auto_detect(self, emit_update=True, detect_modulation=True, detect_noise=False) -> bool: kwargs = {"noise": None if detect_noise else self.noise_threshold, "modulation": None if detect_modulation - else "OOK" if self.__modulation_order == 2 and self.__modulation_type == 0 - else self.modulation_type_str} + else "OOK" if self.bits_per_symbol == 1 and self.modulation_type == "ASK" + else self.modulation_type} estimated_params = AutoInterpretation.estimate(self.iq_array, **kwargs) if estimated_params is None: @@ -372,7 +363,7 @@ def auto_detect(self, emit_update=True, detect_modulation=True, detect_noise=Fal self.noise_threshold = estimated_params["noise"] if detect_modulation: - self.modulation_type_str = estimated_params["modulation_type"] + self.modulation_type = estimated_params["modulation_type"] self.qad_center = estimated_params["center"] self.tolerance = estimated_params["tolerance"] @@ -420,7 +411,7 @@ def eliminate(self): self._qad = None self.parameter_cache.clear() - def silent_set_modulation_type(self, mod_type: int): + def silent_set_modulation_type(self, mod_type: str): self.__modulation_type = mod_type def insert_data(self, index: int, data: np.ndarray): diff --git a/src/urh/ui/actions/ChangeSignalParameter.py b/src/urh/ui/actions/ChangeSignalParameter.py index cb7e07552b..6223303225 100644 --- a/src/urh/ui/actions/ChangeSignalParameter.py +++ b/src/urh/ui/actions/ChangeSignalParameter.py @@ -1,6 +1,5 @@ import copy -import numpy as np from PyQt5.QtWidgets import QUndoCommand from urh.signalprocessing.ProtocolAnalyzer import ProtocolAnalyzer @@ -18,13 +17,14 @@ def __init__(self, signal: Signal, protocol: ProtocolAnalyzer, parameter_name: s self.parameter_value = parameter_value self.orig_value = getattr(self.signal, self.parameter_name) - fmt2 = "d" if isinstance(self.orig_value, int) else ".4n" - fmt3 = "d" if isinstance(parameter_value, int) else ".4n" + fmt2 = "d" if isinstance(self.orig_value, int) else ".4n" if isinstance(self.orig_value, float) else "s" + fmt3 = "d" if isinstance(parameter_value, int) else ".4n" if isinstance(parameter_value, float) else "s" signal_name = signal.name[:10] + "..." if len(signal.name) > 10 else signal.name - self.setText(("change {0} of {1} from {2:" + fmt2 + "} to {3:" + fmt3 + "}").format(parameter_name, signal_name, - self.orig_value, - parameter_value)) + self.setText( + ("change {0} of {1} from {2:" + fmt2 + "} to {3:" + fmt3 + "}") + .format(parameter_name, signal_name, self.orig_value, parameter_value) + ) self.protocol = protocol self.orig_messages = copy.deepcopy(self.protocol.messages) diff --git a/src/urh/ui/painting/GridScene.py b/src/urh/ui/painting/GridScene.py index faf7716567..4498c3ada0 100644 --- a/src/urh/ui/painting/GridScene.py +++ b/src/urh/ui/painting/GridScene.py @@ -4,6 +4,7 @@ from urh import constants from urh.ui.painting.ZoomableScene import ZoomableScene +from urh.util import util from urh.util.Formatter import Formatter @@ -17,13 +18,6 @@ def __init__(self, parent=None): super().__init__(parent) self.setSceneRect(0,0,10,10) - def __calc_x_y_scale(self, rect): - view_rect = self.parent().view_rect() if hasattr(self.parent(), "view_rect") else rect - parent_width = self.parent().width() if hasattr(self.parent(), "width") else 750 - scale_x = view_rect.width() / parent_width - scale_y = view_rect.height() / parent_width - return scale_x, scale_y - def drawBackground(self, painter: QPainter, rect: QRectF): # freqs = np.fft.fftfreq(len(w), 1 / self.sample_rate) if self.draw_grid and len(self.frequencies) > 0: @@ -50,7 +44,7 @@ def drawBackground(self, painter: QPainter, rect: QRectF): + [QLineF(rect.left(), y, rect.right(), y) for y in np.arange(top, bottom, y_grid_size)] painter.drawLines(lines) - scale_x, scale_y = self.__calc_x_y_scale(rect) + scale_x, scale_y = util.calc_x_y_scale(rect, self.parent()) painter.scale(scale_x, scale_y) counter = -1 # Counter for Label for every second line @@ -91,7 +85,7 @@ def draw_frequency_marker(self, x_pos, frequency): self.frequency_marker[1].setFont(font) self.frequency_marker[0].setLine(x_pos, y1, x_pos, y2) - scale_x, scale_y = self.__calc_x_y_scale(self.sceneRect()) + scale_x, scale_y = util.calc_x_y_scale(self.sceneRect(), self.parent()) self.frequency_marker[1].setTransform(QTransform.fromScale(scale_x, scale_y), False) self.frequency_marker[1].setText("Tune to " + Formatter.big_value_with_suffix(frequency, decimals=3)) font_metric = QFontMetrics(self.frequency_marker[1].font()) diff --git a/src/urh/ui/painting/ZoomableScene.py b/src/urh/ui/painting/ZoomableScene.py index 4af0986aa8..0171f0997d 100644 --- a/src/urh/ui/painting/ZoomableScene.py +++ b/src/urh/ui/painting/ZoomableScene.py @@ -1,8 +1,10 @@ -from PyQt5.QtGui import QPen -from PyQt5.QtWidgets import QGraphicsScene, QGraphicsRectItem, QGraphicsSceneDragDropEvent +from PyQt5.QtCore import QRectF +from PyQt5.QtGui import QPen, QFont, QTransform, QFontMetrics +from PyQt5.QtWidgets import QGraphicsScene, QGraphicsRectItem, QGraphicsSceneDragDropEvent, QGraphicsSimpleTextItem from urh import constants from urh.ui.painting.HorizontalSelection import HorizontalSelection +from urh.util import util class ZoomableScene(QGraphicsScene): @@ -11,6 +13,14 @@ def __init__(self, parent=None): self.noise_area = None self.ones_area = None self.zeros_area = None + + self.y_mid = 0 + + self.always_show_symbols_legend = False + + self.ones_caption = None + self.zeros_caption = None + self.ones_arrow = None self.zeros_arrow = None self.selection_area = HorizontalSelection(0, 0, 0, 0, fillcolor=constants.SELECTION_COLOR, @@ -35,7 +45,45 @@ def draw_noise_area(self, y, h): self.noise_area.setY(y) self.noise_area.height = h - def draw_sep_area(self, y_mid): + def redraw_legend(self, force_show=False): + if not (force_show or self.always_show_symbols_legend): + if self.zeros_caption is not None: + self.zeros_caption.hide() + if self.ones_caption is not None: + self.ones_caption.hide() + return + + if self.ones_caption is None: + font = QFont() + font.setPointSize(32) + font.setBold(True) + self.ones_caption = self.addSimpleText("1", font) + + if self.zeros_caption is None: + font = QFont() + font.setPointSize(32) + font.setBold(True) + self.zeros_caption = self.addSimpleText("0", font) + + view_rect = self.parent().view_rect() # type: QRectF + + self.ones_caption.show() + self.zeros_caption.show() + padding = view_rect.height() / 20 + scale_x, scale_y = util.calc_x_y_scale(self.ones_area.rect(), self.parent()) + + y_mid = self.y_mid + fm = QFontMetrics(self.ones_caption.font()) + self.ones_caption.setPos(view_rect.x() + view_rect.width() - fm.width("1") * scale_x, + y_mid - fm.height()*scale_y - padding) + self.ones_caption.setTransform(QTransform.fromScale(scale_x, scale_y), False) + + scale_x, scale_y = util.calc_x_y_scale(self.zeros_area.rect(), self.parent()) + + self.zeros_caption.setPos(view_rect.x() + view_rect.width() - fm.width("0") * scale_x, y_mid + padding) + self.zeros_caption.setTransform(QTransform.fromScale(scale_x, scale_y), False) + + def draw_sep_area(self, y_mid, show_symbols=False): x = self.sceneRect().x() y = self.sceneRect().y() h = self.sceneRect().height() @@ -49,7 +97,6 @@ def draw_sep_area(self, y_mid): self.ones_area.setOpacity(constants.SEPARATION_OPACITY) self.ones_area.setPen(QPen(constants.TRANSPARENT_COLOR, 0)) self.addItem(self.ones_area) - else: self.ones_area.show() self.ones_area.setRect(x, y, w, h / 2 + y_mid) @@ -65,6 +112,9 @@ def draw_sep_area(self, y_mid): self.zeros_area.show() self.zeros_area.setRect(x, start, w, (y + h) - start) + self.y_mid = y_mid + self.redraw_legend(show_symbols) + def clear(self): self.noise_area = None self.ones_area = None diff --git a/src/urh/ui/ui_signal_frame.py b/src/urh/ui/ui_signal_frame.py index 410d17376a..f6f15cbb68 100644 --- a/src/urh/ui/ui_signal_frame.py +++ b/src/urh/ui/ui_signal_frame.py @@ -316,18 +316,6 @@ def setupUi(self, SignalFrame): self.horizontalLayout_6.setContentsMargins(0, 0, 0, 0) self.horizontalLayout_6.setSpacing(0) self.horizontalLayout_6.setObjectName("horizontalLayout_6") - self.gvLegend = LegendGraphicView(self.pageSignal) - self.gvLegend.setMinimumSize(QtCore.QSize(0, 150)) - self.gvLegend.setMaximumSize(QtCore.QSize(30, 16777215)) - self.gvLegend.setFrameShape(QtWidgets.QFrame.NoFrame) - self.gvLegend.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) - self.gvLegend.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn) - self.gvLegend.setInteractive(False) - self.gvLegend.setResizeAnchor(QtWidgets.QGraphicsView.AnchorViewCenter) - self.gvLegend.setRubberBandSelectionMode(QtCore.Qt.ContainsItemShape) - self.gvLegend.setOptimizationFlags(QtWidgets.QGraphicsView.DontSavePainterState) - self.gvLegend.setObjectName("gvLegend") - self.horizontalLayout_6.addWidget(self.gvLegend) self.gvSignal = EpicGraphicView(self.pageSignal) self.gvSignal.setEnabled(True) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) @@ -611,7 +599,6 @@ def retranslateUi(self, SignalFrame): from urh.ui.views.EpicGraphicView import EpicGraphicView -from urh.ui.views.LegendGraphicView import LegendGraphicView from urh.ui.views.SpectrogramGraphicView import SpectrogramGraphicView from urh.ui.views.TextEditProtocolView import TextEditProtocolView from . import urh_rc diff --git a/src/urh/ui/views/EditableGraphicView.py b/src/urh/ui/views/EditableGraphicView.py index 1fcf4edbe3..1ebed49139 100644 --- a/src/urh/ui/views/EditableGraphicView.py +++ b/src/urh/ui/views/EditableGraphicView.py @@ -68,6 +68,14 @@ def __init__(self, parent=None): self.save_as_action.setShortcutContext(Qt.WidgetWithChildrenShortcut) self.addAction(self.save_as_action) + self.show_symbol_legend_action = QAction(self.tr("Show symbol legend"), self) + self.show_symbol_legend_action.setShortcut("L") + self.show_symbol_legend_action.triggered.connect(self.toggle_symbol_legend) + self.show_symbol_legend_action.setShortcutContext(Qt.WidgetWithChildrenShortcut) + self.show_symbol_legend_action.setCheckable(True) + self.show_symbol_legend_action.setChecked(False) + self.addAction(self.show_symbol_legend_action) + self.insert_sine_action = QAction(self.tr("Insert sine wave..."), self) font = self.insert_sine_action.font() font.setBold(True) @@ -209,11 +217,18 @@ def create_context_menu(self): export_demod_action = menu.addAction("Export demodulated...") export_demod_action.triggered.connect(self.export_demodulated_clicked.emit) + menu.addAction(self.show_symbol_legend_action) + return menu def clear_horizontal_selection(self): self.set_horizontal_selection(0, 0) + def toggle_symbol_legend(self): + if self.scene_type == 1: + self.scene().always_show_symbols_legend = self.show_symbol_legend_action.isChecked() + self.scene().draw_sep_area(self.y_sep) + @pyqtSlot() def on_insert_sine_action_triggered(self): if self.something_is_selected: diff --git a/src/urh/ui/views/SelectableGraphicView.py b/src/urh/ui/views/SelectableGraphicView.py index 6fc6030d9b..871ed0ced7 100644 --- a/src/urh/ui/views/SelectableGraphicView.py +++ b/src/urh/ui/views/SelectableGraphicView.py @@ -152,7 +152,7 @@ def mouseMoveEvent(self, event: QMouseEvent): y = self.sceneRect().y() h = self.sceneRect().height() if y < y_sep < y + h: - self.scene().draw_sep_area(y_sep) + self.scene().draw_sep_area(y_sep, show_symbols=True) self.sep_area_moving.emit(y_sep) elif self.is_pos_in_separea(self.mapToScene(event.pos())): self.setCursor(Qt.SplitVCursor) diff --git a/src/urh/ui/views/ZoomableGraphicView.py b/src/urh/ui/views/ZoomableGraphicView.py index eb953c8973..7af92421ee 100644 --- a/src/urh/ui/views/ZoomableGraphicView.py +++ b/src/urh/ui/views/ZoomableGraphicView.py @@ -179,6 +179,9 @@ def redraw_view(self, reinitialize=False): start, end = vr.x(), vr.x() + vr.width() self.scene_manager.show_scene_section(start, end, *self._get_sub_path_ranges_and_colors(start, end)) + if self.scene_type == 1: + self.scene().redraw_legend() + def _get_sub_path_ranges_and_colors(self, start: float, end: float): # Overwritten in Epic Graphic View return None, None diff --git a/src/urh/util/Errors.py b/src/urh/util/Errors.py index ed953e41c0..f38156950c 100644 --- a/src/urh/util/Errors.py +++ b/src/urh/util/Errors.py @@ -1,7 +1,10 @@ import sys +import traceback + from PyQt5.QtWidgets import QMessageBox, QWidget from urh.util.Formatter import Formatter +from urh.util.Logger import logger class Errors: @@ -14,6 +17,14 @@ def generic_error(title: str, msg: str, detailed_msg: str = None): "\n", "
") QMessageBox.critical(w, title, msg) + @staticmethod + def exception(exception: Exception): + logger.exception(exception) + w = QWidget() + msg = "Error: " + str(exception).replace("\n", "
") + "

" + msg += traceback.format_exc().replace("\n", "
") + QMessageBox.critical(w, "An error occurred", msg) + @staticmethod def no_device(): w = QWidget() diff --git a/src/urh/util/ProjectManager.py b/src/urh/util/ProjectManager.py index 036af8f05f..a401b58c41 100644 --- a/src/urh/util/ProjectManager.py +++ b/src/urh/util/ProjectManager.py @@ -487,7 +487,11 @@ def read_project_file_for_signal(self, signal: Signal): signal.noise_threshold = float(sig_tag.get("noise_threshold", 0.1)) signal.sample_rate = float(sig_tag.get("sample_rate", 1e6)) signal.bit_len = int(sig_tag.get("bit_length", 100)) - signal.modulation_type = int(sig_tag.get("modulation_type", 0)) + try: + # Legacy support when modulation type was integer + signal.modulation_type = Signal.MODULATION_TYPES[int(sig_tag.get("modulation_type", 0))] + except (ValueError, IndexError): + signal.modulation_type = sig_tag.get("modulation_type", "ASK") signal.pause_threshold = int(sig_tag.get("pause_threshold", 8)) signal.message_length_divisor = int(sig_tag.get("message_length_divisor", 1)) break diff --git a/src/urh/util/util.py b/src/urh/util/util.py index ddbdedbebc..aa8492ff75 100644 --- a/src/urh/util/util.py +++ b/src/urh/util/util.py @@ -452,3 +452,14 @@ def set_splitter_stylesheet(splitter: QSplitter): "stop:0.5 rgba({0}, {1}, {2}, {3})," "stop:0.8 rgba(255, 255, 255, 0));" "image: url(:/icons/icons/splitter_handle_vertical.svg);}}".format(r, g, b, a)) + + +def calc_x_y_scale(rect, parent): + view_rect = parent.view_rect() if hasattr(parent, "view_rect") else rect + parent_width = parent.width() if hasattr(parent, "width") else 750 + parent_height = parent.height() if hasattr(parent, "height") else 300 + + scale_x = view_rect.width() / parent_width + scale_y = view_rect.height() / parent_height + + return scale_x, scale_y diff --git a/tests/PlotTests.py b/tests/PlotTests.py index 06f3df1fbe..4eaada092f 100644 --- a/tests/PlotTests.py +++ b/tests/PlotTests.py @@ -14,7 +14,7 @@ class PlotTests(unittest.TestCase): def test_plot(self): modulator = Modulator("gfsk") - modulator.modulation_type_str = "GFSK" + modulator.modulation_type = "GFSK" modulator.samples_per_symbol = 100 modulator.sample_rate = 1e6 modulator.param_for_one = 20e3 @@ -34,7 +34,7 @@ def test_plot(self): plt.title("Modulated Wave") plt.subplot(2, 1, 2) - qad = signal_functions.afp_demod(np.ascontiguousarray(data), 0, 1) + qad = signal_functions.afp_demod(np.ascontiguousarray(data), 0, "FSK") plt.plot(qad) plt.title("Quad Demod") @@ -42,7 +42,7 @@ def test_plot(self): def test_carrier_auto_detect(self): signal = Signal(get_path_for_data_file("wsp.complex"), "test") - signal.modulation_type = 0 + signal.modulation_type = "ASK" signal.noise_threshold = 0.035 signal.qad_center = 0.0245 signal.bit_len = 25 diff --git a/tests/auto_interpretation/auto_interpretation_test_util.py b/tests/auto_interpretation/auto_interpretation_test_util.py index 6b9bcefb8d..99cb7b7541 100644 --- a/tests/auto_interpretation/auto_interpretation_test_util.py +++ b/tests/auto_interpretation/auto_interpretation_test_util.py @@ -18,7 +18,7 @@ def demodulate(signal_data, mod_type: str, bit_length, center, noise, tolerance, signal.iq_array = IQArray(signal_data.view(np.float32)) else: signal.iq_array = IQArray(signal_data) - signal.modulation_type = signal.MODULATION_TYPES.index(mod_type) + signal.modulation_type = mod_type signal.bit_len = bit_length signal.qad_center = center signal.noise_threshold = noise diff --git a/tests/auto_interpretation/test_center_detection.py b/tests/auto_interpretation/test_center_detection.py index 60bd4df504..41c705fecb 100644 --- a/tests/auto_interpretation/test_center_detection.py +++ b/tests/auto_interpretation/test_center_detection.py @@ -26,7 +26,7 @@ def generate_rectangular_signal(bits: str, bit_len: int): def test_noisy_rect(self): data = Signal(get_path_for_data_file("fsk.complex")).iq_array.data - rect = afp_demod(data, 0.008, 1)[5:15000] + rect = afp_demod(data, 0.008, "FSK")[5:15000] center = detect_center(rect) self.assertGreaterEqual(center, -0.0587) @@ -34,7 +34,7 @@ def test_noisy_rect(self): def test_ask_center_detection(self): data = Signal(get_path_for_data_file("ask.complex")).iq_array.data - rect = afp_demod(data, 0.01111, 0) + rect = afp_demod(data, 0.01111, "ASK") center = detect_center(rect) self.assertGreaterEqual(center, 0) @@ -42,7 +42,7 @@ def test_ask_center_detection(self): def test_enocean_center_detection(self): data = Signal(get_path_for_data_file("enocean.complex")).iq_array.data - rect = afp_demod(data, 0.05, 0) + rect = afp_demod(data, 0.05, "ASK") messages = [rect[2107:5432], rect[20428:23758], rect[44216:47546]] for i, msg in enumerate(messages): @@ -54,7 +54,7 @@ def test_ask_50_center_detection(self): message_indices = [(0, 8000), (18000, 26000), (36000, 44000), (54000, 62000), (72000, 80000)] data = Signal(get_path_for_data_file("ask50.complex")).iq_array.data - rect = afp_demod(data, 0.0509, 0) + rect = afp_demod(data, 0.0509, "ASK") for start, end in message_indices: center = detect_center(rect[start:end]) @@ -63,7 +63,7 @@ def test_ask_50_center_detection(self): def test_homematic_center_detection(self): data = Signal(get_path_for_data_file("homematic.coco"), "").iq_array.data - rect = afp_demod(data, 0.0012, 1) + rect = afp_demod(data, 0.0012, "FSK") msg1 = rect[17719:37861] msg2 = rect[70412:99385] @@ -78,7 +78,7 @@ def test_homematic_center_detection(self): def test_noised_homematic_center_detection(self): data = Signal(get_path_for_data_file("noised_homematic.complex"), "").iq_array.data - rect = afp_demod(data, 0.0, 1) + rect = afp_demod(data, 0.0, "FSK") center = detect_center(rect) @@ -87,14 +87,14 @@ def test_noised_homematic_center_detection(self): def test_fsk_15db_center_detection(self): data = Signal(get_path_for_data_file("FSK15.complex"), "").iq_array.data - rect = afp_demod(data, 0, 1) + rect = afp_demod(data, 0, "FSK") center = detect_center(rect) self.assertGreaterEqual(center, -0.1979) self.assertLessEqual(center, 0.1131) def test_fsk_10db_center_detection(self): data = Signal(get_path_for_data_file("FSK10.complex"), "").iq_array.data - rect = afp_demod(data, 0, 1) + rect = afp_demod(data, 0, "FSK") center = detect_center(rect) self.assertGreaterEqual(center, -0.1413) self.assertLessEqual(center, 0.05) @@ -107,12 +107,12 @@ def test_fsk_live_capture(self): filtered_data = moving_average_filter.apply_fir_filter(data.flatten()).view(np.float32) filtered_data = filtered_data.reshape((len(filtered_data)//2, 2)) - rect = afp_demod(filtered_data, 0.0175, 1) + rect = afp_demod(filtered_data, 0.0175, "FSK") center = detect_center(rect) self.assertGreaterEqual(center, -0.0148, msg="Filtered") self.assertLessEqual(center, 0.01, msg="Filtered") - rect = afp_demod(data, 0.0175, 1) + rect = afp_demod(data, 0.0175, "FSK") center = detect_center(rect) self.assertGreaterEqual(center, -0.02, msg="Original") self.assertLessEqual(center, 0.01, msg="Original") diff --git a/tests/auto_interpretation/test_message_segmentation.py b/tests/auto_interpretation/test_message_segmentation.py index 64b7b7c099..fa0b641774 100644 --- a/tests/auto_interpretation/test_message_segmentation.py +++ b/tests/auto_interpretation/test_message_segmentation.py @@ -47,7 +47,7 @@ def test_message_segmentation_fsk_xavax(self): def test_segmentation_ask_50(self): modulator = Modulator("ask50") - modulator.modulation_type_str = "ASK" + modulator.modulation_type = "ASK" modulator.param_for_zero = 50 modulator.param_for_one = 100 modulator.samples_per_symbol = 100 diff --git a/tests/auto_interpretation/test_modulation_detection.py b/tests/auto_interpretation/test_modulation_detection.py index d825555da5..76bd39716b 100644 --- a/tests/auto_interpretation/test_modulation_detection.py +++ b/tests/auto_interpretation/test_modulation_detection.py @@ -32,7 +32,7 @@ def test_ask50_detection(self): def test_psk_detection(self): modulator = Modulator("") - modulator.modulation_type_str = "PSK" + modulator.modulation_type = "PSK" modulator.param_for_zero = 0 modulator.param_for_one = 180 diff --git a/tests/cli/test_cli_logic.py b/tests/cli/test_cli_logic.py index 4f2463d4b3..b31d23147e 100644 --- a/tests/cli/test_cli_logic.py +++ b/tests/cli/test_cli_logic.py @@ -13,7 +13,7 @@ def test_cli_modulate_messages(self): modulator = Modulator("test") modulator.sample_rate = 2e3 modulator.samples_per_symbol = 100 - modulator.modulation_type_str = "ASK" + modulator.modulation_type = "ASK" modulator.param_for_zero = 0 modulator.param_for_one = 100 diff --git a/tests/cli/test_cli_parsing.py b/tests/cli/test_cli_parsing.py index 8495bd94fb..df3c445758 100644 --- a/tests/cli/test_cli_parsing.py +++ b/tests/cli/test_cli_parsing.py @@ -27,7 +27,7 @@ def test_build_modulator_from_args(self): args = self.parser.parse_args("--device HackRF --frequency 433.92e6 --sample-rate 2e6" " -p0 0 -p1 1 -mo ASK -cf 1337e3 -ca 0.9 -bl 24 -cp 30".split()) modulator = urh_cli.build_modulator_from_args(args) - self.assertEqual(modulator.modulation_type_str, "ASK") + self.assertEqual(modulator.modulation_type, "ASK") self.assertEqual(modulator.sample_rate, 2e6) self.assertEqual(modulator.samples_per_symbol, 24) self.assertEqual(modulator.param_for_zero, 0) @@ -45,7 +45,7 @@ def test_build_modulator_from_args(self): args = self.parser.parse_args("--device HackRF --frequency 433.92e6 --sample-rate 2e6" " -p0 20e3 -p1=-20e3 -mo FSK -cf 1337e3 -ca 0.9 -bl 24 -cp 30".split()) modulator = urh_cli.build_modulator_from_args(args) - self.assertEqual(modulator.modulation_type_str, "FSK") + self.assertEqual(modulator.modulation_type, "FSK") self.assertEqual(modulator.param_for_zero, 20e3) self.assertEqual(modulator.param_for_one, -20e3) diff --git a/tests/performance/modulator_performance.py b/tests/performance/modulator_performance.py index 85fc1e2e4a..4b5df9fbce 100644 --- a/tests/performance/modulator_performance.py +++ b/tests/performance/modulator_performance.py @@ -6,7 +6,7 @@ def test_fsk_performance(): bit_data = "10" * 100 + "0000011111" + "001101011" * 100 + "111111100000" * 100 modulator = Modulator("Perf") - modulator.modulation_type_str = "FSK" + modulator.modulation_type = "FSK" t = time.time() result = modulator.modulate(bit_data, pause=10000000) elapsed = time.time() - t @@ -18,7 +18,7 @@ def test_fsk_performance(): def test_ask_performance(): bit_data = "10" * 100 + "0000011111" + "001101011" * 100 + "111111100000" * 1000 modulator = Modulator("Perf") - modulator.modulation_type_str = "ASK" + modulator.modulation_type = "ASK" t = time.time() result = modulator.modulate(bit_data, pause=10000000) elapsed = time.time() - t @@ -30,7 +30,7 @@ def test_ask_performance(): def test_psk_performance(): bit_data = "10" * 100 + "0000011111" + "001101011" * 100 + "111111100000" * 1000 modulator = Modulator("Perf") - modulator.modulation_type_str = "PSK" + modulator.modulation_type = "PSK" t = time.time() result = modulator.modulate(bit_data, pause=10000000) elapsed = time.time() - t @@ -41,7 +41,7 @@ def test_psk_performance(): def test_gfsk_performance(): bit_data = "10" * 100 + "0000011111" + "001101011" * 100 + "111111100000" * 100 modulator = Modulator("Perf") - modulator.modulation_type_str = "GFSK" + modulator.modulation_type = "GFSK" t = time.time() result = modulator.modulate(bit_data, pause=10000000) elapsed = time.time() - t diff --git a/tests/test_demodulations.py b/tests/test_demodulations.py index 7f6b41a49a..aeac57b339 100644 --- a/tests/test_demodulations.py +++ b/tests/test_demodulations.py @@ -1,6 +1,9 @@ +import array import unittest from tests.utils_testing import get_path_for_data_file +from urh.cythonext.signal_functions import modulate_c +from urh.signalprocessing.IQArray import IQArray from urh.signalprocessing.ProtocolAnalyzer import ProtocolAnalyzer from urh.signalprocessing.Signal import Signal @@ -8,9 +11,9 @@ class TestDemodulations(unittest.TestCase): def test_ask(self): signal = Signal(get_path_for_data_file("ask.complex"), "ASK-Test") - signal.modulation_type = 0 + signal.modulation_type = "ASK" signal.bit_len = 295 - signal.qad_center = -0.1667 + signal.qad_center = 0.0219 self.assertEqual(signal.num_samples, 13710) proto_analyzer = ProtocolAnalyzer(signal) @@ -19,7 +22,7 @@ def test_ask(self): def test_ask_two(self): signal = Signal(get_path_for_data_file("ask_short.complex"), "ASK-Test2") - signal.modulation_type = 0 + signal.modulation_type = "ASK" signal.noise_threshold = 0.0299 signal.bit_len = 16 signal.qad_center = 0.1300 @@ -32,7 +35,7 @@ def test_ask_two(self): def test_fsk(self): signal = Signal(get_path_for_data_file("fsk.complex"), "FSK-Test") - signal.modulation_type = 1 + signal.modulation_type = "FSK" signal.bit_len = 100 signal.qad_center = 0 @@ -43,7 +46,7 @@ def test_fsk(self): def test_psk(self): signal = Signal(get_path_for_data_file("psk_gen_noisy.complex"), "PSK-Test") - signal.modulation_type = 2 + signal.modulation_type = "PSK" signal.bit_len = 300 signal.qad_center = 0.0281 signal.noise_threshold = 0 @@ -52,3 +55,18 @@ def test_psk(self): proto_analyzer = ProtocolAnalyzer(signal) proto_analyzer.get_protocol_from_signal() self.assertEqual(proto_analyzer.plain_bits_str[0], "101100") + + def test_4_fsk(self): + bits = array.array("B", [1, 0, 1, 0, 1, 1, 0, 0, 0, 1]) + parameters = array.array("f", [-20e3, -10e3, 10e3, 20e3]) + result = modulate_c(bits, 100, "FSK", parameters, 2, 1, 40e3, 0, 1e6, 1000, 0, parameters[0]) + + signal = Signal("") + signal.iq_array = IQArray(result) + signal.bits_per_symbol = 2 + signal.qad_center = 0 + + proto_analyzer = ProtocolAnalyzer(signal) + proto_analyzer.get_protocol_from_signal() + self.assertEqual(proto_analyzer.plain_bits_str[0], "1010110001") + diff --git a/tests/test_generator.py b/tests/test_generator.py index 3fd6c931f1..18b2197931 100644 --- a/tests/test_generator.py +++ b/tests/test_generator.py @@ -52,7 +52,7 @@ def test_generation(self): # Generate Datafile modulator = gframe.modulators[0] - modulator.modulation_type = 0 + modulator.modulation_type = "ASK" modulator.samples_per_symbol = 300 buffer = gframe.prepare_modulation_buffer(gframe.total_modulated_samples, show_error=False) modulated_data = gframe.modulate_data(buffer) diff --git a/tests/test_modulator.py b/tests/test_modulator.py index 735bfc6718..d7cf3fc968 100644 --- a/tests/test_modulator.py +++ b/tests/test_modulator.py @@ -24,12 +24,12 @@ def setUp(self): def test_ask_fsk_psk_modulation(self): modulations = ["ASK", "FSK", "PSK"] - for i, modulation in enumerate(modulations): + for modulation in modulations: modulator = Modulator(modulation) tmp_dir = QDir.tempPath() filename = "{0}_mod.complex".format(modulation) filename = os.path.join(tmp_dir, filename) - modulator.modulation_type = i + modulator.modulation_type = modulation modulator.samples_per_symbol = self.samples_per_symbol if modulation == "ASK": @@ -45,7 +45,7 @@ def test_ask_fsk_psk_modulation(self): modulator.modulate(self.modulation_data, self.pause).tofile(filename) signal = Signal(filename, modulation) - signal.modulation_type = i + signal.modulation_type = modulation signal.bit_len = self.samples_per_symbol if modulation == "ASK": signal.qad_center = 0.5 @@ -63,14 +63,14 @@ def test_gfsk(self): target_file = os.path.join(tempfile.gettempdir(), "test.complex") modulator = Modulator("gfsk") - modulator.modulation_type_str = "FSK" + modulator.modulation_type = "GFSK" modulator.samples_per_symbol = 100 modulator.sample_rate = 1e6 modulator.param_for_one = 20e3 modulator.param_for_zero = -10e3 data1 = modulator.modulate([True, False, False, True, False], 9437) data2 = modulator.modulate([True, False, True], 9845) #, start=len(s)) - data3 = modulator.modulate([True, False, True, False], 8457) #, start=len(s)) + data3 = modulator.modulate([True, False, True, False], 8458) #, start=len(s)) s = np.concatenate((data1, data2, data3)) s.tofile(target_file) @@ -81,7 +81,7 @@ def test_gfsk(self): def test_performance(self): t = time.time() modulator = Modulator("Perf") - modulator.modulation_type = 1 + modulator.modulation_type = "FSK" modulator.modulate([True] * 1000, pause=10000000) elapsed = time.time() - t self.assertLess(elapsed, 0.5) diff --git a/tests/test_project_manager.py b/tests/test_project_manager.py index 1f9c0c1bd5..673a1c72b5 100644 --- a/tests/test_project_manager.py +++ b/tests/test_project_manager.py @@ -34,7 +34,7 @@ def test_save_modulations(self): self.gframe.modulators[0].carrier_amplitude = amplitude self.gframe.modulators[0].carrier_freq_hz = 1337 self.gframe.modulators[0].carrier_phase_deg = 42 - self.gframe.modulators[0].modulation_type = 1 + self.gframe.modulators[0].modulation_type = "FSK" self.gframe.modulators[0].sample_rate = 10 ** 3 self.gframe.modulators.append(Modulator("test 2")) self.gframe.modulators = self.gframe.modulators[:2] # Take only the first two @@ -48,7 +48,7 @@ def test_save_modulations(self): self.assertEqual(loaded_mods[1].name, "test 2") self.assertEqual(loaded_mods[0].carrier_freq_hz, 1337) self.assertEqual(loaded_mods[0].carrier_phase_deg, 42) - self.assertEqual(loaded_mods[0].modulation_type, 1) + self.assertEqual(loaded_mods[0].modulation_type, "FSK") self.assertEqual(loaded_mods[0].sample_rate, 10 ** 3) self.gframe.modulators.clear() diff --git a/tests/test_protocol_analyzer.py b/tests/test_protocol_analyzer.py index 52cc58bd68..bca3e55506 100644 --- a/tests/test_protocol_analyzer.py +++ b/tests/test_protocol_analyzer.py @@ -11,7 +11,7 @@ class TestProtocolAnalyzer(unittest.TestCase): def test_get_bit_sample_pos(self): signal = Signal(get_path_for_data_file("ASK_mod.complex"), "Bit sample pos test") - signal.modulation_type = 0 + signal.modulation_type = "ASK" signal.bit_len = 100 proto_analyzer = ProtocolAnalyzer(signal) @@ -41,7 +41,7 @@ def test_fsk_freq_detection(self): def test_get_rssi_of_message(self): signal = Signal(get_path_for_data_file("two_participants.coco"), "RSSI-Test") - signal.modulation_type = 1 + signal.modulation_type = "FSK" signal.bit_len = 100 signal.qad_center = -0.0507 diff --git a/tests/test_protocol_sniffer.py b/tests/test_protocol_sniffer.py index 0d097ae347..a6d82cd2a3 100644 --- a/tests/test_protocol_sniffer.py +++ b/tests/test_protocol_sniffer.py @@ -24,7 +24,7 @@ def test_protocol_sniffer(self): center = 0.0942 noise = 0.1 tolerance = 2 - modulation_type = 1 + modulation_type = "FSK" sample_rate = 1e6 device_name = NetworkSDRInterfacePlugin.NETWORK_SDR_NAME sniffer = ProtocolSniffer(bit_len=bit_len, center=center, noise=noise, tolerance=tolerance, diff --git a/tests/test_signal_tab_GUI.py b/tests/test_signal_tab_GUI.py index cfb736b90c..bb32b0d4a3 100644 --- a/tests/test_signal_tab_GUI.py +++ b/tests/test_signal_tab_GUI.py @@ -198,21 +198,6 @@ def test_selection_sync(self): self.assertAlmostEqual((128440 - 89383) / 1000000, (frame.ui.gvSignal.view_rect().width()) / 1000000, places=1) - def test_legend_graphic_view(self): - self.add_signal_to_form("esaver.coco") - frame = self.form.signal_tab_controller.signal_frames[0] - - self.assertTrue(frame.ui.gvLegend.isHidden()) - frame.ui.cbSignalView.setCurrentIndex(1) - self.assertFalse(frame.ui.gvLegend.isHidden()) - - self.assertAlmostEqual(frame.ui.gvLegend.y_sep, -frame.ui.spinBoxCenterOffset.value(), places=4) - - frame.ui.spinBoxCenterOffset.setValue(0) - frame.ui.spinBoxCenterOffset.editingFinished.emit() - - self.assertEqual(frame.ui.gvLegend.y_sep, 0) - def test_auto_detect_button(self): self.add_signal_to_form("esaver.coco") frame = self.form.signal_tab_controller.signal_frames[0] @@ -244,8 +229,6 @@ def test_demodulated_view(self): frame.ui.cbSignalView.setCurrentIndex(1) QApplication.instance().processEvents() self.assertEqual(frame.ui.gvSignal.scene_type, 1) - if self.SHOW: - self.assertTrue(frame.ui.gvLegend.isVisible()) def test_context_menu_text_edit_protocol_view(self): self.add_signal_to_form("esaver.coco") diff --git a/tests/test_simulator_dialog.py b/tests/test_simulator_dialog.py index 198cd452f8..76dec62acd 100644 --- a/tests/test_simulator_dialog.py +++ b/tests/test_simulator_dialog.py @@ -86,7 +86,7 @@ def test_set_sniff_parameters(self): self.assertEqual(simulator.sniffer.signal.noise_threshold_relative, 0.1234) sniff_settings_widget.ui.combox_sniff_Modulation.setCurrentText("PSK") - self.assertEqual(simulator.sniffer.signal.modulation_type_str, "PSK") + self.assertEqual(simulator.sniffer.signal.modulation_type, "PSK") decodings = [sniff_settings_widget.ui.comboBox_sniff_encoding.itemText(i) for i in range(sniff_settings_widget.ui.comboBox_sniff_encoding.count())] diff --git a/tests/test_util.py b/tests/test_util.py index 023cff1df7..f9ca02b9c2 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -55,7 +55,7 @@ def test_get_receive_buffer_size(self): def test_write_pcap(self): signal = Signal(get_path_for_data_file("ask.complex"), "ASK-Test") - signal.modulation_type = 0 + signal.modulation_type = "ASK" signal.bit_len = 295 signal.qad_center = -0.1667 self.assertEqual(signal.num_samples, 13710)