diff --git a/data/ui/signal_frame.ui b/data/ui/signal_frame.ui
index 666b9629a7..4e536ab61d 100644
--- a/data/ui/signal_frame.ui
+++ b/data/ui/signal_frame.ui
@@ -84,7 +84,7 @@
- Signal View:
+ Signal view:
@@ -282,7 +282,7 @@
<html><head/><body><p>For <span style=" font-weight:600;">higher order</span> modulations (> 1 Bits/Symbol), there are <span style=" font-weight:600;">multiple</span> centers. We assume that the <span style=" font-weight:600;">spacing</span> between all possible symbols is <span style=" font-weight:600;">constant</span>. Therefore you configure the spacing between centers.</p></body></html>
- Center Spacing:
+ Center spacing:
@@ -351,7 +351,7 @@
<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=" font-weight:400; font-style:italic;">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>
- Error Tolerance:
+ Error tolerance:
@@ -405,7 +405,7 @@
- <html><head/><body><p>Choose the view of your signal. Analog, Demodulated or Spectrogram.</p><p>The quadrature demodulation uses a <span style=" font-weight:600;">threshold of magnitudes,</span> to <span style=" font-weight:600;">suppress noise</span>. All samples with a magnitude lower than this threshold will be eliminated after demodulation.</p><p>Tune this value by selecting a <span style=" font-style:italic;">noisy area</span> and mark it as noise using <span style=" font-weight:600;">context menu</span>.</p></body></html>
+ <html><head/><body><p>Choose the view of your signal. Analog, Demodulated, Spectrogram or I/Q view.</p><p>The quadrature demodulation uses a <span style=" font-weight:600;">threshold of magnitudes,</span> to <span style=" font-weight:600;">suppress noise</span>. All samples with a magnitude lower than this threshold will be eliminated after demodulation.</p><p>Tune this value by selecting a <span style=" font-style:italic;">noisy area</span> and mark it as noise using <span style=" font-weight:600;">context menu</span>.</p><p><span style=" font-weight:600;">Signal colors</span>:<br/>I => <span style=" font-weight:600; color:#3232e1;">blue</span><br/>Q => <span style=" font-weight:600;">black</span></p></body></html>
-
@@ -422,6 +422,11 @@
Spectrogram
+ -
+
+ I/Q view
+
+
-
@@ -545,7 +550,7 @@
If this is set to true, your selected protocol bits will show up in the signal view, and vice versa.
- Sync Selection
+ Sync selection
true
@@ -574,7 +579,7 @@
If you want your protocol to be better separated, edit the PauseLen using right-click menu from a selection in SignalView or ProtocolView.
- Show Signal as
+ Show data as
@@ -660,7 +665,7 @@ If you want your protocol to be better separated, edit the PauseLen using right-
-
- FFT Window Size:
+ FFT window size:
diff --git a/src/urh/controller/widgets/SignalFrame.py b/src/urh/controller/widgets/SignalFrame.py
index 90217e9964..cb086078a5 100644
--- a/src/urh/controller/widgets/SignalFrame.py
+++ b/src/urh/controller/widgets/SignalFrame.py
@@ -685,8 +685,8 @@ def on_noise_threshold_changed(self):
self.ui.spinBoxNoiseTreshold.setValue(self.signal.noise_threshold_relative)
minimum = self.signal.noise_min_plot
maximum = self.signal.noise_max_plot
- if self.ui.cbSignalView.currentIndex() == 0:
- # Draw Noise only in Analog View
+ if self.ui.cbSignalView.currentIndex() == 0 or self.ui.cbSignalView.currentIndex() == 3:
+ # Draw Noise only in analog and I/Q view
self.ui.gvSignal.scene().draw_noise_area(minimum, maximum - minimum)
@pyqtSlot(int)
diff --git a/src/urh/settings.py b/src/urh/settings.py
index 0e35a8a5d9..1419eaae5d 100644
--- a/src/urh/settings.py
+++ b/src/urh/settings.py
@@ -42,6 +42,8 @@ def get_qt_settings_filename():
TRANSPARENT_COLOR = QColor(Qt.transparent)
LINECOLOR = QColor.fromRgb(225, 225, 225)
+LINECOLOR_I = QColor.fromRgb(50, 50, 225)
+LINECOLOR_Q = QColor.fromRgb(55, 53, 53)
BGCOLOR = QColor.fromRgb(55, 53, 53)
AXISCOLOR = QColor.fromRgb(200, 200, 200, 100)
ARROWCOLOR = QColor.fromRgb(204, 120, 50)
diff --git a/src/urh/signalprocessing/Signal.py b/src/urh/signalprocessing/Signal.py
index 22e55bb988..0a13e37f12 100644
--- a/src/urh/signalprocessing/Signal.py
+++ b/src/urh/signalprocessing/Signal.py
@@ -379,6 +379,13 @@ def real_plot_data(self):
except AttributeError:
return np.zeros(0, dtype=np.float32)
+ @property
+ def imag_plot_data(self):
+ try:
+ return self.iq_array.imag
+ except AttributeError:
+ return np.zeros(0, dtype=np.float32)
+
@property
def changed(self) -> bool:
"""
diff --git a/src/urh/ui/painting/SceneManager.py b/src/urh/ui/painting/SceneManager.py
index e108d1df15..2f9c931878 100644
--- a/src/urh/ui/painting/SceneManager.py
+++ b/src/urh/ui/painting/SceneManager.py
@@ -28,11 +28,12 @@ def plot_data(self, value):
@property
def num_samples(self):
+ if isinstance(self.plot_data, list):
+ return len(self.plot_data[0])
return len(self.plot_data)
def show_scene_section(self, x1: float, x2: float, subpath_ranges=None, colors=None):
"""
-
:param x1: start of section to show
:param x2: end of section to show
:param subpath_ranges: for coloring subpaths
@@ -44,12 +45,21 @@ def show_scene_section(self, x1: float, x2: float, subpath_ranges=None, colors=N
start, end = self.__limit_value(x1), self.__limit_value(x2)
if end > start:
- paths = path_creator.create_path(self.plot_data, start=start, end=end,
- subpath_ranges=subpath_ranges)
- self.set_path(paths, colors=colors)
-
- def set_path(self, paths: list, colors=None):
- self.clear_path()
+ if isinstance(self.plot_data, list):
+ paths_i = path_creator.create_path(self.plot_data[0], start=start, end=end,
+ subpath_ranges=subpath_ranges)
+ paths_q = path_creator.create_path(self.plot_data[1], start=start, end=end,
+ subpath_ranges=subpath_ranges)
+ self.set_path(paths_i, colors=[settings.LINECOLOR_I] * len(paths_i))
+ self.set_path(paths_q, colors=[settings.LINECOLOR_Q] * len(paths_q), run_clear_path=False)
+ else:
+ paths = path_creator.create_path(self.plot_data, start=start, end=end,
+ subpath_ranges=subpath_ranges)
+ self.set_path(paths, colors=colors)
+
+ def set_path(self, paths: list, colors=None, run_clear_path=True):
+ if run_clear_path:
+ self.clear_path()
colors = [settings.LINECOLOR] * len(paths) if colors is None else colors
assert len(paths) == len(colors)
for path, color in zip(paths, colors):
@@ -67,7 +77,10 @@ def init_scene(self):
if self.num_samples == 0:
return
- minimum, maximum = IQArray.min_max_for_dtype(self.plot_data.dtype)
+ if isinstance(self.plot_data, list):
+ minimum, maximum = IQArray.min_max_for_dtype(self.plot_data[0].dtype)
+ else:
+ minimum, maximum = IQArray.min_max_for_dtype(self.plot_data.dtype)
self.scene.setSceneRect(0, minimum, self.num_samples, maximum - minimum)
self.scene.setBackgroundBrush(settings.BGCOLOR)
diff --git a/src/urh/ui/painting/SignalSceneManager.py b/src/urh/ui/painting/SignalSceneManager.py
index 5fcc501126..d0a312a458 100644
--- a/src/urh/ui/painting/SignalSceneManager.py
+++ b/src/urh/ui/painting/SignalSceneManager.py
@@ -8,17 +8,24 @@ class SignalSceneManager(SceneManager):
def __init__(self, signal: Signal, parent):
super().__init__(parent)
self.signal = signal
- self.scene_type = 0 # 0 = Analog Signal, 1 = QuadDemodView
+ self.scene_type = 0 # 0 = Analog Signal, 1 = QuadDemodView, 2 = Spectogram, 3 = I/Q view
self.mod_type = "ASK"
def show_scene_section(self, x1: float, x2: float, subpath_ranges=None, colors=None):
- self.plot_data = self.signal.real_plot_data if self.scene_type == 0 else self.signal.qad
+ if self.scene_type == 0:
+ self.plot_data = self.signal.real_plot_data
+ elif self.scene_type == 3:
+ self.plot_data = [self.signal.imag_plot_data, self.signal.real_plot_data]
+ else:
+ self.plot_data = self.signal.qad
super().show_scene_section(x1, x2, subpath_ranges=subpath_ranges, colors=colors)
def init_scene(self):
if self.scene_type == 0:
# Ensure real plot has same y Axis
self.plot_data = self.signal.real_plot_data
+ elif self.scene_type == 3:
+ self.plot_data = [self.signal.imag_plot_data, self.signal.real_plot_data]
else:
self.plot_data = self.signal.qad
@@ -28,7 +35,7 @@ def init_scene(self):
self.line_item.setLine(0, 0, 0, 0) # Hide Axis
- if self.scene_type == 0:
+ if self.scene_type == 0 or self.scene_type == 3:
self.scene.draw_noise_area(self.signal.noise_min_plot, self.signal.noise_max_plot - self.signal.noise_min_plot)
else:
self.scene.draw_sep_area(-self.signal.center_thresholds)
diff --git a/src/urh/ui/ui_signal_frame.py b/src/urh/ui/ui_signal_frame.py
index b1937c6bab..6f172b4b72 100644
--- a/src/urh/ui/ui_signal_frame.py
+++ b/src/urh/ui/ui_signal_frame.py
@@ -2,7 +2,8 @@
#
#
-# WARNING! All changes made in this file will be lost!
+# WARNING: Any manual changes made to this file will be lost when pyuic5 is
+# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets
@@ -165,6 +166,7 @@ def setupUi(self, SignalFrame):
self.cbSignalView.addItem("")
self.cbSignalView.addItem("")
self.cbSignalView.addItem("")
+ self.cbSignalView.addItem("")
self.gridLayout_2.addWidget(self.cbSignalView, 17, 1, 1, 1)
self.spinBoxNoiseTreshold = QtWidgets.QDoubleSpinBox(SignalFrame)
self.spinBoxNoiseTreshold.setSuffix("")
@@ -566,7 +568,7 @@ def setupUi(self, SignalFrame):
def retranslateUi(self, SignalFrame):
_translate = QtCore.QCoreApplication.translate
SignalFrame.setWindowTitle(_translate("SignalFrame", "Frame"))
- self.lSignalViewText.setText(_translate("SignalFrame", "Signal View:"))
+ self.lSignalViewText.setText(_translate("SignalFrame", "Signal view:"))
self.spinBoxBitsPerSymbol.setToolTip(_translate("SignalFrame", "
Higher order modulations can carry multiple bits with each symbol. Configure how many bits are represented by a symbol. (Default = Binary modulation with one bit per symbol)
"))
self.btnSaveSignal.setText(_translate("SignalFrame", "..."))
self.btnCloseSignal.setText(_translate("SignalFrame", "X"))
@@ -578,20 +580,21 @@ def retranslateUi(self, SignalFrame):
self.cbProtoView.setItemText(1, _translate("SignalFrame", "Hex"))
self.cbProtoView.setItemText(2, _translate("SignalFrame", "ASCII"))
self.lCenterSpacing.setToolTip(_translate("SignalFrame", "For higher order modulations (> 1 Bits/Symbol), there are multiple centers. We assume that the spacing between all possible symbols is constant. Therefore you configure the spacing between centers.
"))
- self.lCenterSpacing.setText(_translate("SignalFrame", "Center Spacing:"))
+ self.lCenterSpacing.setText(_translate("SignalFrame", "Center spacing:"))
self.spinBoxTolerance.setToolTip(_translate("SignalFrame", "This is the error tolerance for determining the pulse lengths in the demodulated signal.
Example: Say, we are reading a ones pulse and the tolerance value was set to 5. Then 5 errors (which must follow sequentially) are accepted.
Tune this value if you have spiky data after demodulation.
"))
self.lSamplesPerSymbol.setToolTip(_translate("SignalFrame", "This is the length of one symbol in samples. For binary modulations (default) this is the bit length.
"))
self.lSamplesPerSymbol.setText(_translate("SignalFrame", "Samples/Symbol:"))
self.lErrorTolerance.setToolTip(_translate("SignalFrame", "This is the error tolerance for determining the pulse lengths in the demodulated signal.
Example: Say, we are reading a ones pulse and the tolerance value was set to 5. Then 5 errors (which must follow sequentially) are accepted.
Tune this value if you have spiky data after demodulation.
"))
- self.lErrorTolerance.setText(_translate("SignalFrame", "Error Tolerance:"))
+ self.lErrorTolerance.setText(_translate("SignalFrame", "Error tolerance:"))
self.lCenterOffset.setToolTip(_translate("SignalFrame", "This is the threshold used for determining if a bit is one or zero. You can set it here or grab the middle of the area in Quadrature Demod View.
"))
self.lCenterOffset.setText(_translate("SignalFrame", "Center:"))
self.labelModulation.setToolTip(_translate("SignalFrame", "Choose signals modulation:
- Amplitude Shift Keying (ASK)
- Frequency Shift Keying (FSK)
- Phase Shift Keying (PSK)
"))
self.labelModulation.setText(_translate("SignalFrame", "Modulation:"))
- self.cbSignalView.setToolTip(_translate("SignalFrame", "Choose the view of your signal. Analog, Demodulated or Spectrogram.
The quadrature demodulation uses a threshold of magnitudes, to suppress noise. All samples with a magnitude lower than this threshold will be eliminated after demodulation.
Tune this value by selecting a noisy area and mark it as noise using context menu.
"))
+ self.cbSignalView.setToolTip(_translate("SignalFrame", "Choose the view of your signal. Analog, Demodulated, Spectrogram or I/Q view.
The quadrature demodulation uses a threshold of magnitudes, to suppress noise. All samples with a magnitude lower than this threshold will be eliminated after demodulation.
Tune this value by selecting a noisy area and mark it as noise using context menu.
Signal colors:
I => blue
Q => black
"))
self.cbSignalView.setItemText(0, _translate("SignalFrame", "Analog"))
self.cbSignalView.setItemText(1, _translate("SignalFrame", "Demodulated"))
self.cbSignalView.setItemText(2, _translate("SignalFrame", "Spectrogram"))
+ self.cbSignalView.setItemText(3, _translate("SignalFrame", "I/Q view"))
self.spinBoxNoiseTreshold.setToolTip(_translate("SignalFrame", "Set the noise magnitude of your signal. You can tune this value to mute noise in your signal and reveal the true data.
"))
self.spinBoxCenterSpacing.setToolTip(_translate("SignalFrame", "For higher order modulations (> 1 Bits/Symbol), there are multiple centers. We assume that the spacing between all possible symbols is constant. Therefore you configure the spacing between centers.
"))
self.lineEditSignalName.setText(_translate("SignalFrame", "SignalName"))
@@ -601,17 +604,17 @@ def retranslateUi(self, SignalFrame):
self.cbModulationType.setItemText(2, _translate("SignalFrame", "PSK"))
self.btnAdvancedModulationSettings.setText(_translate("SignalFrame", "..."))
self.chkBoxSyncSelection.setToolTip(_translate("SignalFrame", "If this is set to true, your selected protocol bits will show up in the signal view, and vice versa."))
- self.chkBoxSyncSelection.setText(_translate("SignalFrame", "Sync Selection"))
+ self.chkBoxSyncSelection.setText(_translate("SignalFrame", "Sync selection"))
self.labelSpectrogramMin.setText(_translate("SignalFrame", "Datamin:"))
self.labelSpectrogramMax.setText(_translate("SignalFrame", "Datamax:"))
self.chkBoxShowProtocol.setToolTip(_translate("SignalFrame", "Show the extracted protocol based on the parameters InfoLen, PauseLen and ZeroTreshold (in QuadratureDemod-View).\n"
"\n"
"If you want your protocol to be better separated, edit the PauseLen using right-click menu from a selection in SignalView or ProtocolView."))
- self.chkBoxShowProtocol.setText(_translate("SignalFrame", "Show Signal as"))
+ self.chkBoxShowProtocol.setText(_translate("SignalFrame", "Show data as"))
self.spinBoxSamplesPerSymbol.setToolTip(_translate("SignalFrame", "This is the length of one symbol in samples. For binary modulations (default) this is the bit length.
"))
self.btnAutoDetect.setToolTip(_translate("SignalFrame", "Automatically detect center, bit length and tolerance. You can also choose to additionally detect the noise and modulation when clicking this button.
"))
self.btnAutoDetect.setText(_translate("SignalFrame", "Autodetect parameters"))
- self.labelFFTWindowSize.setText(_translate("SignalFrame", "FFT Window Size:"))
+ self.labelFFTWindowSize.setText(_translate("SignalFrame", "FFT window size:"))
self.spinBoxCenterOffset.setToolTip(_translate("SignalFrame", "This is the threshold used for determining if a bit is one or zero. You can set it here or grab the middle of the area in Quadrature Demod View.
"))
self.labelNoise.setToolTip(_translate("SignalFrame", "Set the noise magnitude of your signal. You can tune this value to mute noise in your signal and reveal the true data.
"))
self.labelNoise.setText(_translate("SignalFrame", "Noise:"))