Skip to content

Commit c1ed0ef

Browse files
authored
add support for already demodulated signals (#780)
Treat single channel wav files as already demodulated data.
1 parent 1d3981c commit c1ed0ef

File tree

6 files changed

+61
-13
lines changed

6 files changed

+61
-13
lines changed

data/azure-pipelines.yml

+1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ jobs:
3030
python -m pip install --upgrade pip twine
3131
pip install -r data/requirements.txt
3232
sudo apt-get install libhackrf-dev librtlsdr-dev xvfb libxkbcommon-x11-0 x11-utils
33+
pip install PyVirtualDisplay!=1.0,!=1.1
3334
pip install twine setuptools wheel pytest pytest-xvfb pytest-cov pytest-faulthandler
3435
python -c "import tempfile, os; open(os.path.join(tempfile.gettempdir(), 'urh_releasing'), 'w').close()"
3536
displayName: 'Install dependencies'

src/urh/controller/MainController.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,8 @@ def add_signal(self, signal, group_id=0, index=-1):
297297
if self.ui.actionAuto_detect_new_signals.isChecked() and not has_entry and not signal.changed:
298298
sig_frame.ui.stackedWidget.setCurrentWidget(sig_frame.ui.pageLoading)
299299
qApp.processEvents()
300-
signal.auto_detect(detect_modulation=True, detect_noise=False)
300+
if not signal.already_demodulated:
301+
signal.auto_detect(detect_modulation=True, detect_noise=False)
301302
sig_frame.ui.stackedWidget.setCurrentWidget(sig_frame.ui.pageSignal)
302303

303304
signal.blockSignals(False)

src/urh/controller/widgets/SignalFrame.py

+36-11
Original file line numberDiff line numberDiff line change
@@ -105,17 +105,22 @@ def __init__(self, proto_analyzer: ProtocolAnalyzer, undo_stack: QUndoStack, pro
105105
self.configure_filter_action.triggered.connect(self.on_configure_filter_action_triggered)
106106
self.ui.btnFilter.setMenu(self.filter_menu)
107107

108-
self.auto_detect_menu = QMenu()
109-
self.detect_noise_action = self.auto_detect_menu.addAction(self.tr("Additionally detect noise"))
110-
self.detect_noise_action.setCheckable(True)
111-
self.detect_noise_action.setChecked(False)
112-
self.detect_modulation_action = self.auto_detect_menu.addAction(self.tr("Additionally detect modulation"))
113-
self.detect_modulation_action.setCheckable(True)
114-
self.detect_modulation_action.setChecked(False)
115-
self.ui.btnAutoDetect.setMenu(self.auto_detect_menu)
108+
if not self.signal.already_demodulated:
109+
self.auto_detect_menu = QMenu()
110+
self.detect_noise_action = self.auto_detect_menu.addAction(self.tr("Additionally detect noise"))
111+
self.detect_noise_action.setCheckable(True)
112+
self.detect_noise_action.setChecked(False)
113+
self.detect_modulation_action = self.auto_detect_menu.addAction(self.tr("Additionally detect modulation"))
114+
self.detect_modulation_action.setCheckable(True)
115+
self.detect_modulation_action.setChecked(False)
116+
self.ui.btnAutoDetect.setMenu(self.auto_detect_menu)
117+
116118

117119
if self.signal.wav_mode:
118-
self.ui.lSignalTyp.setText("Signal (*.wav)")
120+
if self.signal.already_demodulated:
121+
self.ui.lSignalTyp.setText("Demodulated (1-channel *.wav)")
122+
else:
123+
self.ui.lSignalTyp.setText("Signal (*.wav)")
119124
else:
120125
self.ui.lSignalTyp.setText("Complex Signal")
121126

@@ -149,6 +154,16 @@ def __init__(self, proto_analyzer: ProtocolAnalyzer, undo_stack: QUndoStack, pro
149154

150155
self.show_protocol(refresh=False)
151156

157+
if self.signal.already_demodulated:
158+
self.ui.cbModulationType.hide()
159+
self.ui.labelModulation.hide()
160+
self.ui.labelNoise.hide()
161+
self.ui.spinBoxNoiseTreshold.hide()
162+
self.ui.btnAutoDetect.hide()
163+
self.ui.cbSignalView.setCurrentIndex(1)
164+
self.ui.cbSignalView.hide()
165+
self.ui.lSignalViewText.hide()
166+
152167
else:
153168
self.ui.lSignalTyp.setText("Protocol")
154169
self.set_empty_frame_visibilities()
@@ -748,8 +763,18 @@ def on_cb_signal_view_index_changed(self):
748763
def on_btn_autodetect_clicked(self):
749764
self.ui.btnAutoDetect.setEnabled(False)
750765
self.setCursor(Qt.WaitCursor)
751-
success = self.signal.auto_detect(detect_modulation=self.detect_modulation_action.isChecked(),
752-
detect_noise=self.detect_noise_action.isChecked())
766+
767+
try:
768+
detect_modulation = self.detect_modulation_action.isChecked()
769+
except AttributeError:
770+
detect_modulation = False
771+
772+
try:
773+
detect_noise = self.detect_noise_action.isChecked()
774+
except AttributeError:
775+
detect_noise = False
776+
success = self.signal.auto_detect(detect_modulation=detect_modulation, detect_noise=detect_noise)
777+
753778
self.ui.btnAutoDetect.setEnabled(True)
754779
self.unsetCursor()
755780
if not success:

src/urh/signalprocessing/Signal.py

+11-1
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ def __init__(self, filename: str, name="Signal", modulation: str = None, sample_
6363

6464
self.__parameter_cache = {mod: {"center": None, "samples_per_symbol": None} for mod in self.MODULATION_TYPES}
6565

66+
self.__already_demodulated = False
67+
6668
if len(filename) > 0:
6769
if self.wav_mode:
6870
self.__load_wav_file(filename)
@@ -110,6 +112,7 @@ def __load_wav_file(self, filename: str):
110112
self.iq_array = IQArray(None, np.float32, n=num_frames)
111113
if num_channels == 1:
112114
self.iq_array.real = np.multiply(1 / params["max"], np.subtract(data, params["center"]))
115+
self.__already_demodulated = True
113116
elif num_channels == 2:
114117
self.iq_array.real = np.multiply(1 / params["max"], np.subtract(data[0::2], params["center"]))
115118
self.iq_array.imag = np.multiply(1 / params["max"], np.subtract(data[1::2], params["center"]))
@@ -128,6 +131,10 @@ def __load_compressed_complex(self, filename: str):
128131
self.__load_complex_file(extracted_filename)
129132
os.remove(extracted_filename)
130133

134+
@property
135+
def already_demodulated(self) -> bool:
136+
return self.__already_demodulated
137+
131138
@property
132139
def sample_rate(self):
133140
return self.__sample_rate
@@ -313,7 +320,10 @@ def noise_threshold_relative(self, value: float):
313320
@property
314321
def qad(self):
315322
if self._qad is None:
316-
self._qad = self.quad_demod()
323+
if self.already_demodulated:
324+
self._qad = np.ascontiguousarray(self.real_plot_data, dtype=self.real_plot_data.dtype)
325+
else:
326+
self._qad = self.quad_demod()
317327

318328
return self._qad
319329

tests/data/demodulated.wav

3.26 KB
Binary file not shown.

tests/test_signal_tab_GUI.py

+11
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,17 @@ def test_context_menu_text_edit_protocol_view(self):
243243
menu = text_edit.create_context_menu()
244244
self.assertEqual(len([action for action in menu.actions() if action.text() == "Participant"]), 1)
245245

246+
def test_load_already_demodulated(self):
247+
self.add_signal_to_form("demodulated.wav")
248+
assert isinstance(self.form, MainController)
249+
250+
sig_frame = self.form.signal_tab_controller.signal_frames[0]
251+
sig_frame.ui.cbProtoView.setCurrentText("Hex")
252+
sig_frame.ui.spinBoxCenterOffset.setValue(0.0459)
253+
sig_frame.ui.spinBoxCenterOffset.editingFinished.emit()
254+
255+
self.assertTrue(sig_frame.ui.txtEdProto.toPlainText().startswith("abcd"))
256+
246257
def test_export_demodulated(self):
247258
self.add_signal_to_form("esaver.complex16s")
248259
assert isinstance(self.form, MainController)

0 commit comments

Comments
 (0)