diff --git a/icons/5652.svg b/icons/5652.svg new file mode 100644 index 0000000000000000000000000000000000000000..36a4a37b0b778a3c936f790d18c4f9d26c6b9e17 --- /dev/null +++ b/icons/5652.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" x="0px" y="0px" viewBox="0 0 100 100" enable-background="new 0 0 100 100" xml:space="preserve"><g display="none"><polygon display="inline" points="85.982,15.043 14.018,15.043 41.006,42.031 41.006,84.957 58.996,72.963 58.996,42.031 "></polygon></g><g display="none"><path display="inline" d="M76.592,85.935l-11.32-17.052l7.006-6.496V15.922c0-1.024-0.832-1.856-1.859-1.856H29.314 c-1.027,0-1.861,0.832-1.861,1.856v46.465l7.17,6.644L23.408,85.935h6.404l8.775-13.227l0.07,0.064h22.414l0.238-0.221 l8.875,13.383H76.592z M62.004,64.233c-2.355,0-4.266-1.907-4.266-4.27c0-2.356,1.91-4.266,4.266-4.266 c2.357,0,4.27,1.909,4.27,4.266C66.273,62.326,64.361,64.233,62.004,64.233z M43.463,17.634h12.805v4.406H43.463V17.634z M33.859,26.169h32.012V45.38H33.859V26.169z M38.525,64.233c-2.357,0-4.268-1.907-4.268-4.27c0-2.356,1.91-4.266,4.268-4.266 c2.359,0,4.271,1.909,4.271,4.266C42.797,62.326,40.885,64.233,38.525,64.233z"></path></g><g><path d="M77.845,26.948c-6.625-7.896-16.55-12.932-27.689-12.932c-19.975,0-36.138,16.107-36.138,35.984h14.395 c0-11.961,9.765-21.691,21.786-21.691c7.191,0,13.567,3.501,17.538,8.867l-8.464,8.088l26.71-0.012V18.667L77.845,26.948z"></path><path d="M49.799,71.687c-7.193,0-13.565-3.5-17.539-8.867l8.464-8.086l-26.706,0.012V81.33l8.134-8.281 c6.625,7.896,16.551,12.935,27.69,12.935c19.978,0,36.141-16.11,36.141-35.986H71.584C71.584,61.956,61.819,71.687,49.799,71.687z"></path></g><g display="none"><polygon display="inline" points="32.01,14.02 67.99,50.002 32.01,85.98 "></polygon></g></svg> \ No newline at end of file diff --git a/lib/gui/gui.py b/lib/gui/gui.py index bd43808ac2d33e75b13128c847292b14ddadc4dd..c6b9baa0b63721c67381db68f511b2660fcdc899 100644 --- a/lib/gui/gui.py +++ b/lib/gui/gui.py @@ -3,26 +3,75 @@ import sys import os import logging +import datetime from PyQt5 import QtCore, QtGui, QtWidgets, uic -class SuGui(QtWidgets.QMainWindow): +import lib.hardware as hardware +class SuGui(QtWidgets.QMainWindow): + """ + GUI class used to realise the main window. + """ def __init__(self): + """ + Instantiate GUI class and open window. + GUI elements are directly loaded and converted from UI file. + """ super(SuGui, self).__init__() - uic.loadUi('lib/gui/su_mainwindow.ui', self) + uic.loadUi("lib/gui/su_mainwindow.ui", self) self.show() + # setting some defaults + self.lineEdit_dump_file.setText(self._get_default_dump_file_path()) + self.lineEdit_dump_file.setCursorPosition(0) + self.refresh_hw_button() # button events - self.button_start.clicked.connect(self.buttonStart) - self.button_stop.clicked.connect(self.buttonStop) - self.button_close.clicked.connect(self.buttonClose) + self.button_start.clicked.connect(self.start_button) + self.button_stop.clicked.connect(self.stop_button) + self.button_close.clicked.connect(self.close_button) + self.button_hw_refresh.clicked.connect(self.refresh_hw_button) - def buttonStart(self): - pass + def start_button(self): + """ + Start button action in main window. + """ + if len(self.lineEdit_dump_file.text()) == 0: + QtWidgets.QMessageBox.about(self, "Dumpfile path missing…", \ + "Please insert dump file path. ") + else: + pass - def buttonStop(self): + def stop_button(self): + """ + Stop button action in main window. + """ pass - def buttonClose(self): + def close_button(self): + """ + Close button action in main window. + """ logging.info("Close application") QtCore.QCoreApplication.instance().quit() + + def refresh_hw_button(self): + """ + Refresh combo box with current connected hardware. + """ + self.comboBox_hardware.clear() + for device in hardware.Hardware().get_connected_supported_devices(): + text = device['id'] + ": " + device['name'] + self.comboBox_hardware.insertItem(0, self.tr(text)) + + def _get_default_dump_file_path(self): + """ + Generate path for dump file. The default file path is defined as + dump_file with current timestamp located in the home of current user. + + Example: + /home/user/su/dump_file-1942_05_23-13_37_23.iq + """ + dt = datetime.datetime.now().strftime("%Y_%m_%d-%H_%M_%S") + path = os.path.expanduser("~") + "/su/dump_file-" + dt + ".iq" + + return path diff --git a/lib/gui/su_mainwindow.ui b/lib/gui/su_mainwindow.ui index 0e5c26c366637a27f2189d7b003ba8752a269a02..8ad99a5110b25e48e394ae6987592347ce42410c 100644 --- a/lib/gui/su_mainwindow.ui +++ b/lib/gui/su_mainwindow.ui @@ -1,13 +1,13 @@ <?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> - <class>SuMainWindow</class> - <widget class="QMainWindow" name="SuMainWindow"> + <class>SU::MainWindow</class> + <widget class="QMainWindow" name="SU::MainWindow"> <property name="geometry"> <rect> <x>0</x> <y>0</y> - <width>572</width> - <height>363</height> + <width>563</width> + <height>317</height> </rect> </property> <property name="windowTitle"> @@ -18,7 +18,7 @@ <property name="geometry"> <rect> <x>410</x> - <y>80</y> + <y>30</y> <width>141</width> <height>31</height> </rect> @@ -31,7 +31,7 @@ <property name="geometry"> <rect> <x>10</x> - <y>20</y> + <y>10</y> <width>101</width> <height>61</height> </rect> @@ -49,7 +49,7 @@ </rect> </property> <property name="text"> - <string>433 MHz</string> + <string extracomment="433e6">433 MHz</string> </property> </widget> <widget class="QRadioButton" name="rb_868"> @@ -62,7 +62,7 @@ </rect> </property> <property name="text"> - <string>868 MHz</string> + <string extracomment="868e6">868 MHz</string> </property> <property name="checked"> <bool>true</bool> @@ -73,9 +73,9 @@ <property name="geometry"> <rect> <x>10</x> - <y>190</y> + <y>170</y> <width>120</width> - <height>131</height> + <height>121</height> </rect> </property> <property name="title"> @@ -91,7 +91,7 @@ </rect> </property> <property name="text"> - <string>1 min</string> + <string extracomment="60">1 min</string> </property> </widget> <widget class="QRadioButton" name="rb_5m"> @@ -104,7 +104,7 @@ </rect> </property> <property name="text"> - <string>5 min</string> + <string extracomment="300">5 min</string> </property> </widget> <widget class="QRadioButton" name="rb_10m"> @@ -117,7 +117,10 @@ </rect> </property> <property name="text"> - <string>10 min</string> + <string extracomment="600">10 min</string> + </property> + <property name="checked"> + <bool>true</bool> </property> </widget> <widget class="QRadioButton" name="rb_1h_2"> @@ -130,11 +133,14 @@ </rect> </property> <property name="text"> - <string>1h</string> + <string extracomment="36000">1h</string> </property> - <property name="checked"> + <property name="checkable"> <bool>true</bool> </property> + <property name="checked"> + <bool>false</bool> + </property> </widget> <widget class="QRadioButton" name="rb_1h"> <property name="geometry"> @@ -146,7 +152,7 @@ </rect> </property> <property name="text"> - <string>24 h</string> + <string extracomment="864000">24 h</string> </property> </widget> </widget> @@ -154,7 +160,7 @@ <property name="geometry"> <rect> <x>410</x> - <y>40</y> + <y>70</y> <width>141</width> <height>31</height> </rect> @@ -167,9 +173,9 @@ <property name="geometry"> <rect> <x>10</x> - <y>90</y> + <y>80</y> <width>101</width> - <height>91</height> + <height>81</height> </rect> </property> <property name="title"> @@ -185,7 +191,7 @@ </rect> </property> <property name="text"> - <string>1 MHz</string> + <string extracomment="1e6">1 MHz</string> </property> </widget> <widget class="QRadioButton" name="rb_3mhz"> @@ -198,7 +204,7 @@ </rect> </property> <property name="text"> - <string>3 MHz</string> + <string extracomment="3e6">3 MHz</string> </property> </widget> <widget class="QRadioButton" name="rb_2mhz"> @@ -211,7 +217,7 @@ </rect> </property> <property name="text"> - <string>2 MHz</string> + <string extracomment="2e6">2 MHz</string> </property> <property name="checked"> <bool>true</bool> @@ -221,10 +227,10 @@ <widget class="QGroupBox" name="box_iq_dump_file"> <property name="geometry"> <rect> - <x>160</x> - <y>20</y> + <x>150</x> + <y>80</y> <width>241</width> - <height>91</height> + <height>81</height> </rect> </property> <property name="title"> @@ -274,7 +280,7 @@ <property name="geometry"> <rect> <x>410</x> - <y>280</y> + <y>260</y> <width>141</width> <height>31</height> </rect> @@ -286,10 +292,10 @@ <widget class="QLabel" name="label_status"> <property name="geometry"> <rect> - <x>160</x> - <y>150</y> - <width>211</width> - <height>61</height> + <x>410</x> + <y>110</y> + <width>141</width> + <height>131</height> </rect> </property> <property name="text"> @@ -299,13 +305,76 @@ <enum>Qt::PlainText</enum> </property> </widget> + <widget class="QGroupBox" name="box_hardware"> + <property name="geometry"> + <rect> + <x>150</x> + <y>170</y> + <width>241</width> + <height>61</height> + </rect> + </property> + <property name="title"> + <string>RF hardware</string> + </property> + <widget class="QComboBox" name="comboBox_hardware"> + <property name="geometry"> + <rect> + <x>10</x> + <y>30</y> + <width>181</width> + <height>22</height> + </rect> + </property> + </widget> + <widget class="QPushButton" name="button_hw_refresh"> + <property name="geometry"> + <rect> + <x>200</x> + <y>30</y> + <width>31</width> + <height>21</height> + </rect> + </property> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset> + <normaloff>../../icons/5652.svg</normaloff>../../icons/5652.svg</iconset> + </property> + </widget> + </widget> + <widget class="QGroupBox" name="box_env"> + <property name="geometry"> + <rect> + <x>150</x> + <y>10</y> + <width>201</width> + <height>61</height> + </rect> + </property> + <property name="title"> + <string>Select environment</string> + </property> + <widget class="QComboBox" name="comboBox_env"> + <property name="geometry"> + <rect> + <x>10</x> + <y>30</y> + <width>181</width> + <height>22</height> + </rect> + </property> + </widget> + </widget> </widget> <widget class="QMenuBar" name="menubar"> <property name="geometry"> <rect> <x>0</x> <y>0</y> - <width>572</width> + <width>563</width> <height>19</height> </rect> </property> diff --git a/lib/hardware.py b/lib/hardware.py new file mode 100644 index 0000000000000000000000000000000000000000..c6aa48ec15a5bfe7d8d5ae0cdacc81528a9308ee --- /dev/null +++ b/lib/hardware.py @@ -0,0 +1,73 @@ +# -*- encoding: utf-8 -*- + +import re +import subprocess +import sys +import os +import logging + +class Hardware(object): + """ + Inspect system for supported via USB connected devices. + + Currently supportd devices: + * HackRF One + * LTR-SDR DVB-T stick + """ + SUPPORTED_HARDWARE = { + "hackrfone": "1d50:cc15", + "rtlsdr": "0bda:2838", + } + + def get_usb_devices(self): + """ + Parse lsusb output and return a list of dictionaries. + """ + devices = [] + + lsusb_output = subprocess.check_output("lsusb", shell=True) + for line in lsusb_output.split("\n"): + if re.match("^Bus", line): + device = self._parse_lsusb_line(line) + devices.append(device) + else: + continue + + return devices + + def get_connected_supported_devices(self): + """ + Get all connected devices + """ + devices = self.get_usb_devices() + + connected_devices = [] + for device in devices: + if self._supported_device(self, device): + connected_devices.append(device) + else: + continue + + return connected_devices + + @staticmethod + def _parse_lsusb_line(line): + """ + Splitt lsusb line in useful key values. + """ + device = {} + device_unsorted = line.split(" ") + + device['bus'] = device_unsorted[1] + device['device'] = device_unsorted[3] + device['id'] = device_unsorted[5] + device['name'] = " ".join(device_unsorted[6:]) + + return device + + @staticmethod + def _supported_device(self, device): + """ + Check if a given device is supported. + """ + return device['id'] in self.SUPPORTED_HARDWARE.values()