Skip to content
Snippets Groups Projects
Commit c674ae59 authored by Daniel Meißner's avatar Daniel Meißner Committed by Daniel Meißner
Browse files

added noise floor detection and plot

parent d8298244
No related branches found
No related tags found
No related merge requests found
import sys
import os
import tempfile
import numpy as np
import matplotlib.pyplot as plt
from gnuradio import blocks
from gnuradio import gr
import osmosdr
class NoiseFloorDetect(gr.top_block):
def __init__(self,
samp_rate,
freq,
capture_time,
device=None
):
gr.top_block.__init__(self, "Top Block")
self.samp_rate = samp_rate
self.freq = freq
self.capture_time = int(capture_time)
self.device = device
self.msgq_out = self.blocks_message_sink_0_msgq_out = gr.msg_queue(0)
self._build_blocks()
def detect(self):
"""
"""
self.start()
samples_count = 0
sample_blocks = []
minimum = 999999999.9
maximum = 0.0
while True:
# pull from message queue
msg = self.msgq_out.delete_head()
msg_string = msg.to_string()
# convert string with floats into an numpy array
samples = np.fromstring(msg_string, dtype='float32')
sample_blocks.append(samples)
samples_count += len(samples)
if samples_count >= self.capture_time*self.samp_rate:
break
samples = np.concatenate(sample_blocks)
statistic_results = self._extract_statistic_data_from_samples(samples)
plot_results = self._plot_data(samples, self.samp_rate)
results = statistic_results
results['plot_path'] = plot_results['plot_path']
results['threshold'] = plot_results['threshold']
return results
def get_samp_rate(self):
return self.samp_rate
def set_samp_rate(self, samp_rate):
self.samp_rate = samp_rate
self.blocks_throttle_0.set_sample_rate(self.samp_rate)
def _plot_data(self, samples, samp_rate=2e6):
result = {}
# 1. split samples into 1s sections and extract max value
# 2. threshold is calculated as median + min value
max_sections = []
for i in range(0,int(len(samples)/2e6)-1):
max = np.amax(samples[i*int(samp_rate):(i+1)*int(samp_rate)])
max_sections.append(max)
median = np.median(max_sections)
min = np.amin(max_sections)
result['threshold'] = round(median+min, 6)
plt.plot(samples)
plt.plot([0, len(samples)-1], \
[result['threshold'], result['threshold']], \
label="threshold " + str(result['threshold']), \
linewidth=1.5, linestyle="-", color="red")
plt.legend(loc='upper right')
plt.xlabel("Samples")
plt.ylabel("Magnitude")
result['plot_path'] = self._create_image_tmp_file()
plt.savefig(result['plot_path'].name)
plt.close()
return result
def _create_image_tmp_file(self):
return tempfile.NamedTemporaryFile()
def _extract_statistic_data_from_samples(self, samples):
measurements = {}
measurements['min'] = np.amin(samples)
measurements['max'] = np.amax(samples)
measurements['average'] = np.average(samples)
measurements['median'] = np.median(samples)
measurements['std'] = np.std(samples)
measurements['seconds'] = len(samples)/self.samp_rate
return measurements
def _build_blocks(self):
self.osmosdr_source_0 = osmosdr.source( args="numchan=" + str(1) + " " + "osmosdr=0" )
self.osmosdr_source_0.set_sample_rate(self.samp_rate)
self.osmosdr_source_0.set_center_freq(self.freq, 0)
self.osmosdr_source_0.set_freq_corr(0, 0)
self.osmosdr_source_0.set_dc_offset_mode(0, 0)
self.osmosdr_source_0.set_iq_balance_mode(0, 0)
self.osmosdr_source_0.set_gain_mode(False, 0)
self.osmosdr_source_0.set_gain(0, 0)
self.osmosdr_source_0.set_if_gain(20, 0)
self.osmosdr_source_0.set_bb_gain(20, 0)
self.osmosdr_source_0.set_antenna("", 0)
self.osmosdr_source_0.set_bandwidth(0, 0)
self.blocks_head_0 = blocks.head(gr.sizeof_gr_complex*1, \
int(self.samp_rate)*int(self.capture_time))
self.blocks_throttle_0 = blocks.throttle(gr.sizeof_gr_complex*1, \
10*self.samp_rate, \
True)
self.blocks_message_sink_0 = blocks.message_sink(gr.sizeof_float*1, \
self.blocks_message_sink_0_msgq_out, \
False)
self.blocks_file_source_0 = self.osmosdr_source_0
self.blocks_complex_to_mag_squared_0 = blocks.complex_to_mag_squared(1)
##################################################
# Connections
##################################################
self.connect((self.blocks_file_source_0, 0), (self.blocks_throttle_0, 0))
self.connect((self.blocks_throttle_0, 0), (self.blocks_head_0, 0))
self.connect((self.blocks_head_0, 0), (self.blocks_complex_to_mag_squared_0, 0))
self.connect((self.blocks_complex_to_mag_squared_0, 0), \
(self.blocks_message_sink_0, 0))
\ No newline at end of file
# -*- coding: utf-8 -*-
import os
import scipy
import matplotlib.pyplot as plt
import numpy as np
from lib.disk import Disk
class RenderSpecgram(object):
def __init__(self, source, destination):
self.source = source
self.destination = destination
......@@ -5,6 +5,7 @@ import os
import logging
import datetime
import re
import time
from PyQt5 import QtCore, QtGui, QtWidgets, uic
......@@ -13,8 +14,11 @@ from lib.su import Su
from lib.disk import Disk
from lib.gr.file_sink import FileSink
from lib.gr.noise_detect import NoiseFloorDetect
from lib.gui.update_progress_bar import UpdateProgressBar
from lib.gui.capture import Capture
from lib.gui.noise_floor_plot import NoiseFloorPlot
class SuGui(QtWidgets.QMainWindow):
"""
......@@ -38,6 +42,7 @@ class SuGui(QtWidgets.QMainWindow):
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_nf_detect.clicked.connect(self.nf_button)
self.button_hw_refresh.clicked.connect(self.refresh_hw_button)
self.comboBox_env.currentIndexChanged.connect(self.configure_env)
self.rb_868.toggled.connect(self._check_samp_rate)
......@@ -48,6 +53,8 @@ class SuGui(QtWidgets.QMainWindow):
# edit lines
self.lineEdit_dump_file.editingFinished. \
connect(self.check_dump_file_path)
self.lineEdit_nf.editingFinished. \
connect(self.check_nf)
# radio buttons
self.radio_buttons_samp_rate_and_time = {
# samp_rate
......@@ -80,7 +87,6 @@ class SuGui(QtWidgets.QMainWindow):
self.progress_bar_thread = UpdateProgressBar()
self.capture_thread = Capture()
def start_button(self):
"""
Start button action in main window.
......@@ -172,6 +178,35 @@ class SuGui(QtWidgets.QMainWindow):
"Contact: daniel.meissner@smail.inf.h-brs.de\n" \
"Source: https://git.fslab.de/dmeiss2s/su")
def nf_button(self):
if len(Hardware.get_connected_supported_devices()) != 0:
options = self._extract_options()
measure_time = 60
nf_detect = NoiseFloorDetect(options['samp_rate'],
options['freq'],
measure_time)
QtWidgets.QMessageBox.about(self, "Noise floor detection started", \
"You have to wait " + str(measure_time) + "s after clicking 'Ok'.")
result = nf_detect.detect()
# plot results
self.lineEdit_nf.setText(str(result['threshold']))
self.nf_plot = NoiseFloorPlot(result['plot_path'].name + ".png")
string = ""
for key,value in result.iteritems():
if key == "plot_path":
continue
string += str(key) + ": " + str(value) + "\n"
self.nf_plot.label_data.setText(string)
self.nf_plot.show()
def nf_plot_result(self, result):
print("Returned nf detect thread.")
self.nf_result = result
def check_dump_file_path(self):
"""
Update labels for writable state and free disk space in path.
......@@ -190,6 +225,18 @@ class SuGui(QtWidgets.QMainWindow):
else:
self.label_disk_space.setStyleSheet('color: black')
def check_nf(self):
"""
"""
try:
nf = float(self.lineEdit_nf.text())
except:
QtWidgets.QMessageBox.about(self, "Error", \
"Threshold needs to be from type int or float.")
self.lineEdit_nf.setText("0.006")
def configure_env(self):
"""
Configure envirenment with selected home automatation system.
......@@ -230,7 +277,7 @@ class SuGui(QtWidgets.QMainWindow):
self.rb_868.setChecked(True)
self.rb_433.setChecked(False)
def _set_setEnabled(self, status = True, freq_button_only = False):
def _set_setEnabled(self, status=True, freq_button_only=False):
"""
Enable or disable buttons the user can interact with.
......@@ -251,6 +298,7 @@ class SuGui(QtWidgets.QMainWindow):
self.comboBox_hardware.setEnabled(status)
self.comboBox_env.setEnabled(status)
self.button_hw_refresh.setEnabled(status)
self.button_nf_detect.setEnabled(status)
def _get_default_dump_file_path(self):
......
# -*- coding: utf-8 -*-
from PyQt5 import QtCore, QtGui, QtWidgets, uic
class NoiseFloorPlot(QtWidgets.QMainWindow):
def __init__(self, plot_path):
super(NoiseFloorPlot, self).__init__()
uic.loadUi("lib/gui/su_nf_plot.ui", self)
self.plot_path = plot_path
self.setWindowTitle("Noise floor detection plot - %s" % self.plot_path)
self.button_close.clicked.connect(self.close_button)
self.pixmap = QtGui.QPixmap(self.plot_path)
self.label_image_plot.setPixmap(self.pixmap)
self.label_image_plot.show()
def close_button(self):
"""
Close button action
"""
self.close()
\ No newline at end of file
......@@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>563</width>
<height>317</height>
<height>348</height>
</rect>
</property>
<property name="windowTitle">
......@@ -73,7 +73,7 @@
<property name="geometry">
<rect>
<x>10</x>
<y>170</y>
<y>190</y>
<width>120</width>
<height>121</height>
</rect>
......@@ -243,7 +243,7 @@
</rect>
</property>
<property name="title">
<string>I/Q dump file</string>
<string>I/Q dump path</string>
</property>
<widget class="QLineEdit" name="lineEdit_dump_file">
<property name="geometry">
......@@ -289,7 +289,7 @@
<property name="geometry">
<rect>
<x>410</x>
<y>260</y>
<y>280</y>
<width>141</width>
<height>31</height>
</rect>
......@@ -304,7 +304,7 @@
<x>410</x>
<y>110</y>
<width>141</width>
<height>131</height>
<height>171</height>
</rect>
</property>
<property name="text">
......@@ -393,6 +393,42 @@
<number>0</number>
</property>
</widget>
<widget class="QGroupBox" name="groupBox">
<property name="geometry">
<rect>
<x>150</x>
<y>260</y>
<width>241</width>
<height>61</height>
</rect>
</property>
<property name="title">
<string>Noise floor threshold</string>
</property>
<widget class="QLineEdit" name="lineEdit_nf">
<property name="geometry">
<rect>
<x>10</x>
<y>30</y>
<width>131</width>
<height>22</height>
</rect>
</property>
</widget>
<widget class="QPushButton" name="button_nf_detect">
<property name="geometry">
<rect>
<x>150</x>
<y>30</y>
<width>81</width>
<height>22</height>
</rect>
</property>
<property name="text">
<string>Detect</string>
</property>
</widget>
</widget>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
......
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SU::NFPlot</class>
<widget class="QMainWindow" name="SU::NFPlot">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>988</width>
<height>668</height>
</rect>
</property>
<property name="windowTitle">
<string/>
</property>
<widget class="QWidget" name="centralWidget">
<widget class="QPushButton" name="button_close">
<property name="geometry">
<rect>
<x>840</x>
<y>320</y>
<width>141</width>
<height>31</height>
</rect>
</property>
<property name="text">
<string>Close</string>
</property>
</widget>
<widget class="QLabel" name="label_image_plot">
<property name="geometry">
<rect>
<x>20</x>
<y>10</y>
<width>801</width>
<height>641</height>
</rect>
</property>
<property name="text">
<string/>
</property>
</widget>
<widget class="QLabel" name="label_data">
<property name="geometry">
<rect>
<x>840</x>
<y>10</y>
<width>141</width>
<height>291</height>
</rect>
</property>
<property name="text">
<string/>
</property>
<property name="textFormat">
<enum>Qt::PlainText</enum>
</property>
</widget>
</widget>
<action name="actionAbout">
<property name="text">
<string>About</string>
</property>
</action>
<action name="actionQuit">
<property name="text">
<string>Quit</string>
</property>
</action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<tabstops>
<tabstop>button_close</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment