Merge pull request #124 from jopohl/addcustomsine

Addcustomsine
This commit is contained in:
Johannes Pohl
2017-02-05 10:44:05 +01:00
committed by GitHub
12 changed files with 391 additions and 8 deletions

View File

@@ -28,7 +28,7 @@ COMPILER_DIRECTIVES = {'language_level': 3,
}
UI_SUBDIRS = ("actions", "delegates", "views")
PLUGINS = ("AmbleAnalyzer", "MessageBreak", "ZeroHide", "NetworkSDRInterface")
PLUGINS = ("AmbleAnalyzer", "MessageBreak", "ZeroHide", "NetworkSDRInterface", "InsertSine")
URH_DIR = "urh"
try:

View File

@@ -0,0 +1,96 @@
import os
import numpy as np
from PyQt5 import uic
from PyQt5.QtCore import pyqtSignal
from PyQt5.QtCore import pyqtSlot
from PyQt5.QtWidgets import QDialog
from urh.plugins.Plugin import SignalEditorPlugin
from urh.util.Formatter import Formatter
class InsertSinePlugin(SignalEditorPlugin):
insert_sine_wave_clicked = pyqtSignal()
def __init__(self):
dirname = os.path.dirname(os.readlink(__file__)) if os.path.islink(__file__) else os.path.dirname(__file__)
self.dialog_ui = uic.loadUi(os.path.realpath(os.path.join(dirname, "insert_sine_dialog.ui"))) # type: QDialog
self.complex_wave = None
self.amplitude = 0.5
self.frequency = 10
self.phase = 0
self.sample_rate = 1e6
self.num_samples = 1e6
self.dialog_ui.doubleSpinBoxAmplitude.setValue(self.amplitude)
self.dialog_ui.doubleSpinBoxFrequency.setValue(self.frequency)
self.dialog_ui.doubleSpinBoxPhase.setValue(self.phase)
self.dialog_ui.doubleSpinBoxSampleRate.setValue(self.sample_rate)
self.dialog_ui.doubleSpinBoxNSamples.setValue(self.num_samples)
self.dialog_ui.labelTime.setText(Formatter.science_time(self.num_samples/self.sample_rate, decimals=3))
super().__init__(name="InsertSine")
def create_connects(self):
pass
def __create_dialog_connects(self):
self.dialog_ui.doubleSpinBoxAmplitude.valueChanged.connect(self.on_double_spin_box_amplitude_value_changed)
self.dialog_ui.doubleSpinBoxFrequency.valueChanged.connect(self.on_double_spin_box_frequency_value_changed)
self.dialog_ui.doubleSpinBoxPhase.valueChanged.connect(self.on_double_spin_box_phase_value_changed)
self.dialog_ui.doubleSpinBoxSampleRate.valueChanged.connect(self.on_double_spin_box_sample_rate_value_changed)
self.dialog_ui.doubleSpinBoxNSamples.valueChanged.connect(self.on_spin_box_n_samples_value_changed)
self.dialog_ui.btnAbort.clicked.connect(self.on_btn_abort_clicked)
self.dialog_ui.btnOK.clicked.connect(self.on_btn_ok_clicked)
def show_insert_sine_dialog(self):
self.dialog_ui.show()
self.__create_dialog_connects()
self.draw_sine_wave()
def draw_sine_wave(self):
self.complex_wave = np.empty(int(self.num_samples), dtype=np.complex64)
t = (np.arange(0, self.num_samples) / self.sample_rate)
self.complex_wave.real = self.amplitude * np.cos(2 * np.pi * self.frequency * t + self.phase)
self.complex_wave.imag = self.amplitude * np.sin(2 * np.pi * self.frequency * t + self.phase)
self.dialog_ui.graphicsViewSineWave.plot_data(self.complex_wave.imag.astype(np.float32))
self.dialog_ui.graphicsViewSineWave.draw_full()
@pyqtSlot(float)
def on_double_spin_box_amplitude_value_changed(self, value: float):
self.amplitude = value
self.draw_sine_wave()
@pyqtSlot(float)
def on_double_spin_box_frequency_value_changed(self, value: float):
self.frequency = value
self.draw_sine_wave()
@pyqtSlot(float)
def on_double_spin_box_phase_value_changed(self, value: float):
self.phase = value
self.draw_sine_wave()
@pyqtSlot(float)
def on_double_spin_box_sample_rate_value_changed(self, value: float):
self.sample_rate = value
self.dialog_ui.labelTime.setText(Formatter.science_time(self.num_samples / self.sample_rate, decimals=3))
self.draw_sine_wave()
@pyqtSlot(float)
def on_spin_box_n_samples_value_changed(self, value: float):
self.num_samples = int(value)
self.dialog_ui.labelTime.setText(Formatter.science_time(self.num_samples / self.sample_rate, decimals=3))
self.draw_sine_wave()
@pyqtSlot()
def on_btn_abort_clicked(self):
self.dialog_ui.close()
@pyqtSlot()
def on_btn_ok_clicked(self):
self.insert_sine_wave_clicked.emit()
self.dialog_ui.close()

View File

View File

@@ -0,0 +1,3 @@
This plugin enables you to insert custom sine waves into your signal.
You will find a new context menu entry in interpretation signal view.
Transform URH into a full fledged signal editor!

View File

@@ -0,0 +1,200 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>DialogCustomSine</class>
<widget class="QDialog" name="DialogCustomSine">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>601</width>
<height>308</height>
</rect>
</property>
<property name="windowTitle">
<string>Insert sine wave</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QSplitter" name="splitter">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<widget class="QWidget" name="layoutWidget">
<layout class="QGridLayout" name="gridLayout">
<item row="4" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Sample Rate:</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="KillerDoubleSpinBox" name="doubleSpinBoxNSamples">
<property name="decimals">
<number>3</number>
</property>
<property name="minimum">
<double>0.000000000000000</double>
</property>
<property name="maximum">
<double>9999999999999999455752309870428160.000000000000000</double>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QDoubleSpinBox" name="doubleSpinBoxPhase">
<property name="suffix">
<string>°</string>
</property>
<property name="decimals">
<number>3</number>
</property>
<property name="maximum">
<double>360.000000000000000</double>
</property>
</widget>
</item>
<item row="8" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="1">
<widget class="QDoubleSpinBox" name="doubleSpinBoxAmplitude">
<property name="decimals">
<number>3</number>
</property>
<property name="maximum">
<double>1.000000000000000</double>
</property>
<property name="singleStep">
<double>0.001000000000000</double>
</property>
<property name="value">
<double>1.000000000000000</double>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Frequency (Hz):</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="KillerDoubleSpinBox" name="doubleSpinBoxFrequency">
<property name="decimals">
<number>3</number>
</property>
<property name="minimum">
<double>0.000000000000000</double>
</property>
<property name="maximum">
<double>9999999999999999931398190359470212947659194368.000000000000000</double>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Phase:</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="KillerDoubleSpinBox" name="doubleSpinBoxSampleRate">
<property name="decimals">
<number>3</number>
</property>
<property name="minimum">
<double>0.000000000000000</double>
</property>
<property name="maximum">
<double>99999999999999991433150857216.000000000000000</double>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Samples:</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Amplitude:</string>
</property>
</widget>
</item>
<item row="3" column="0" colspan="2">
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Time:</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QLabel" name="labelTime">
<property name="text">
<string>20s</string>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QPushButton" name="btnOK">
<property name="text">
<string>Ok</string>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="QPushButton" name="btnAbort">
<property name="text">
<string>Abort</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="ZoomableGraphicView" name="graphicsViewSineWave">
<property name="renderHints">
<set>QPainter::Antialiasing|QPainter::HighQualityAntialiasing|QPainter::TextAntialiasing</set>
</property>
</widget>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>ZoomableGraphicView</class>
<extends>QGraphicsView</extends>
<header>urh.ui.views.ZoomableGraphicView.h</header>
</customwidget>
<customwidget>
<class>KillerDoubleSpinBox</class>
<extends>QDoubleSpinBox</extends>
<header>urh.ui.KillerDoubleSpinBox.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>InsertSineWaveSettings</class>
<widget class="QFrame" name="InsertSineWaveSettings">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>Frame</string>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>No settings available.</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -2,10 +2,9 @@ import socketserver
import threading
import time
from PyQt5.QtCore import QRegExp, pyqtSlot
from PyQt5.QtCore import pyqtSlot
from PyQt5.QtCore import QTimer
from PyQt5.QtCore import pyqtSignal
from PyQt5.QtGui import QRegExpValidator
import socket
from urh.plugins.Plugin import SDRPlugin

View File

@@ -70,4 +70,10 @@ class LabelAssignPlugin(Plugin):
class SDRPlugin(Plugin):
def __init__(self, name: str):
Plugin.__init__(self, name)
Plugin.__init__(self, name)
class SignalEditorPlugin(Plugin):
def __init__(self, name: str):
Plugin.__init__(self, name)

View File

@@ -378,3 +378,4 @@ class Signal(QObject):
self._num_samples = len(self.data)
self.data_edited.emit()
self.protocol_needs_update.emit()
self.changed = True

View File

@@ -5,6 +5,8 @@ from PyQt5.QtWidgets import QAction
from PyQt5.QtWidgets import QApplication, QMenu, QActionGroup
from urh import constants
from urh.plugins.InsertSine.InsertSinePlugin import InsertSinePlugin
from urh.plugins.PluginManager import PluginManager
from urh.ui.ROI import ROI
from urh.ui.views.SelectableGraphicView import SelectableGraphicView
@@ -66,6 +68,16 @@ class EpicGraphicView(SelectableGraphicView):
self.delete_action.triggered.connect(self.on_delete_action_triggered)
self.addAction(self.delete_action)
self.insert_sine_action = QAction(self.tr("Insert sine wave..."))
font = self.insert_sine_action.font()
font.setBold(True)
self.insert_sine_action.setFont(font)
self.insert_sine_action.triggered.connect(self.on_insert_sine_action_triggered)
self.insert_sine_plugin = InsertSinePlugin()
self.insert_sine_plugin.insert_sine_wave_clicked.connect(self.on_insert_sine_wave_clicked)
@property
def signal(self):
return self.parent_frame.signal
@@ -118,6 +130,7 @@ class EpicGraphicView(SelectableGraphicView):
menu = QMenu(self)
menu.addAction(self.save_action)
menu.addAction(self.save_as_action)
menu.addSeparator()
zoom_action = None
create_action = None
@@ -131,6 +144,11 @@ class EpicGraphicView(SelectableGraphicView):
self.paste_action.setEnabled(self.stored_item is not None)
menu.addSeparator()
if PluginManager().is_plugin_enabled("InsertSine"):
menu.addAction(self.insert_sine_action)
if not self.selection_area.is_empty:
menu.addSeparator()
#autorangeAction = menu.addAction(self.tr("Show Autocrop Range"))
if not self.selection_area.is_empty:
@@ -353,3 +371,11 @@ class EpicGraphicView(SelectableGraphicView):
def on_save_as_action_triggered(self):
self.save_as_clicked.emit()
@pyqtSlot()
def on_insert_sine_action_triggered(self):
self.insert_sine_plugin.show_insert_sine_dialog()
@pyqtSlot()
def on_insert_sine_wave_clicked(self):
if self.insert_sine_plugin.complex_wave is not None:
self.signal.insert_data(self.paste_position, self.insert_sine_plugin.complex_wave)

View File

@@ -1,4 +1,5 @@
from PyQt5.QtCore import pyqtSignal, QRectF
from PyQt5.QtGui import QResizeEvent
from PyQt5.QtGui import QWheelEvent
from PyQt5.QtWidgets import QGraphicsScene
@@ -71,4 +72,8 @@ class ZoomableGraphicView(SelectableGraphicView):
if self.scene_creator is not None:
x1 = self.view_rect().x()
x2 = x1 + self.view_rect().width()
self.scene_creator.show_scene_section(x1, x2)
self.scene_creator.show_scene_section(x1, x2)
def resizeEvent(self, event: QResizeEvent):
self.draw_full()
event.accept()

View File

@@ -12,7 +12,7 @@ class Formatter:
@staticmethod
def science_time(time_in_seconds: float) -> str:
def science_time(time_in_seconds: float, decimals=2) -> str:
if time_in_seconds < 1e-6:
suffix = "n"
value = time_in_seconds * 1e9
@@ -26,7 +26,7 @@ class Formatter:
suffix = ""
value = time_in_seconds
return locale.format_string("%.2f " + suffix, value) + "s"
return locale.format_string("%.{0}f ".format(decimals) + suffix, value) + "s"
@staticmethod
def big_value_with_suffix(value: float, decimals=3) -> str:
@@ -47,4 +47,4 @@ class Formatter:
return dtype(str_val)
except (ValueError, TypeError):
logger.warning("The {0} is not a valid {1}, assuming {2}".format(str_val, str(dtype), str(default)))
return default
return default