diff --git a/grc/CMakeLists.txt b/grc/CMakeLists.txt index 4221eea..e298d86 100644 --- a/grc/CMakeLists.txt +++ b/grc/CMakeLists.txt @@ -22,5 +22,6 @@ install(FILES crfa_rds_table_qt.xml crfa_rds_parser_table_qt.xml crfa_rds_decoder.xml - crfa_max_freq.xml DESTINATION share/gnuradio/grc/blocks + crfa_max_freq.xml + crfa_smooth_vectors.xml DESTINATION share/gnuradio/grc/blocks ) diff --git a/grc/crfa_max_freq.xml b/grc/crfa_max_freq.xml index ccdc004..0d012a7 100644 --- a/grc/crfa_max_freq.xml +++ b/grc/crfa_max_freq.xml @@ -10,6 +10,7 @@ * name * key (makes the value accessible as $keyname, e.g. in the make node) * type --> + set_center_freq($center_freq); fft_len fft_len diff --git a/grc/crfa_rds_parser_table_qt.xml b/grc/crfa_rds_parser_table_qt.xml index df29063..4422c6d 100644 --- a/grc/crfa_rds_parser_table_qt.xml +++ b/grc/crfa_rds_parser_table_qt.xml @@ -13,7 +13,7 @@ #end if $(signals) = rds_parser_table_qt_Signals() self.$(id) = crfa.rds_parser_table_qt($(signals),$nPorts) -$(win) = rds_parser_table_qt_Widget($signals, $label) +$(win) = rds_parser_table_qt_Widget($signals, $label,self.$(id)) $(gui_hint()($win)) Label @@ -42,5 +42,10 @@ $(gui_hint()($win)) $nPorts + + freq + message + 1 + diff --git a/grc/crfa_smooth_vectors.xml b/grc/crfa_smooth_vectors.xml new file mode 100644 index 0000000..2250d34 --- /dev/null +++ b/grc/crfa_smooth_vectors.xml @@ -0,0 +1,52 @@ + + + smooth_vectors + crfa_smooth_vectors + [crfa] + import crfa + crfa.smooth_vectors($vec_len, $decim, $moving_avg_len) + + + vec_len + vec_len + 1024 + int + + + decim + decim + 1 + int + + + moving_avg_len + moving_avg_len + 2 + int + + + + + in + float + $vec_len + + + + out + float + $vec_len + + diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index 9bcc146..a9cb4a4 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -35,7 +35,8 @@ GR_PYTHON_INSTALL( qtguitest.py rds_table_qt.py rds_parser_table_qt.py - max_freq.py DESTINATION ${GR_PYTHON_DIR}/crfa + max_freq.py + smooth_vectors.py DESTINATION ${GR_PYTHON_DIR}/crfa ) ######################################################################## diff --git a/python/__init__.py b/python/__init__.py index 9af8807..520d1fd 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -36,4 +36,5 @@ from qtguitest import qtguitest from rds_table_qt import rds_table_qt from rds_parser_table_qt import rds_parser_table_qt from max_freq import max_freq +from smooth_vectors import smooth_vectors # diff --git a/python/chart.py b/python/chart.py new file mode 100644 index 0000000..ce2132a --- /dev/null +++ b/python/chart.py @@ -0,0 +1,482 @@ +from itertools import cycle +from PyQt4.Qt import * + +DEFAULT_COLORS = [0x3366cc, 0xdc3912, 0xff9900, 0x109618, 0x990099, + 0x0099c6, 0xdd4477, 0x66aa00, 0xb82e2e, 0x316395, + 0x994499, 0x22aa99, 0xaaaa11, 0x6633cc, 0x16d620] + +class Chart(object): + def __init__(self, data, colors=None): + self.data = data + self.colors = colors + + self._ref_col = 0 + self._ref_isv = True + + def setVerticalAxisColumn(self, column): + self._ref_col = column + self._ref_isv = True + + def setHorizontalAxisColumn(self, column): + self._ref_col = column + self._ref_isv = False + + def save(self, filename, chart_size, legend_width=None): + image_size = chart_size + if legend_width is not None: + image_size = image_size + QSize(legend_width, 0) + + image = QImage(image_size, QImage.Format_ARGB32_Premultiplied) + painter = QPainter(image) + painter.setRenderHint(QPainter.Antialiasing) + painter.fillRect(image.rect(), Qt.white) + self.draw(painter, QRect(QPoint(0, 0), chart_size)) + if legend_width is not None: + self.drawLegend(painter, QRect(QPoint(chart_size.width(), 10), QSize(legend_width, chart_size.height()))) + painter.end() + return image.save(filename) + + def draw(self, painter, rectangle): + raise NotImplementedError + + def drawLegend(self, painter, rectangle): + SPACE = 2 + + font_metrics = painter.fontMetrics() + size = font_metrics.xHeight() * 2 + + y = SPACE + x0 = SPACE + x1 = x0 + size + SPACE * 3 + + w = rectangle.width() - size - SPACE + tw = w - x1 + + painter.save() + painter.translate(rectangle.x(), rectangle.y()) + + color = self._icolors() + for i, column in enumerate(self._fetchLegendData()): + if (y + size + SPACE * 2) >= (rectangle.y() + rectangle.height()) and i < (len(self.data.columns) - 1): + painter.drawText(x1, y, tw, size, Qt.AlignLeft | Qt.AlignVCenter, "...") + y += size + SPACE + break + + text = font_metrics.elidedText(column, Qt.ElideRight, tw) + painter.fillRect(x0, y, size, size, QColor(next(color))) + painter.drawText(x1, y, tw, size, Qt.AlignLeft | Qt.AlignVCenter, text) + y += size + SPACE + + painter.setPen(Qt.lightGray) + painter.drawRect(0, 0, w, y) + painter.restore() + + def _fetchLegendData(self): + for i, column in enumerate(self.data.columns): + if i != self._ref_col: + yield column + + def _icolors(self): + if self.colors is None: + return cycle(DEFAULT_COLORS) + return cycle(self.colors) + +class PieChart(Chart): + def draw(self, painter, rectangle): + painter.save() + painter.translate(rectangle.x(), rectangle.y()) + + # Calculate Values + vtotal = float(sum(row[not self._ref_col] for row in self.data.rows)) + values = [row[not self._ref_col] / vtotal for row in self.data.rows] + + # Draw Char + start_angle = 90 * 16 + for color, v in zip(self._icolors(), values): + span_angle = v * -360.0 * 16 + painter.setPen(Qt.white) + painter.setBrush(QColor(color)) + painter.drawPie(rectangle, start_angle, span_angle) + start_angle += span_angle + + painter.restore() + + def _fetchLegendData(self): + for row in self.data.rows: + yield row[self._ref_col] + +class ScatterChart(Chart): + SPAN = 10 + + def __init__(self, data, **kwargs): + super(ScatterChart, self).__init__(data, **kwargs) + + self.haxis_title = None + self.haxis_vmin = None + self.haxis_vmax = None + self.haxis_step = None + self.haxis_grid = True + + self.vaxis_title = None + self.vaxis_vmin = None + self.vaxis_vmax = None + self.vaxis_step = None + self.vaxis_grid = True + + def draw(self, painter, rectangle): + self._setupDefaultValues() + + font_metrics = painter.fontMetrics() + h = font_metrics.xHeight() * 2 + x = font_metrics.width(self._vToString(self.vaxis_vmax)) + + # Calculate X steps + nxstep = int(round((self.haxis_vmax - self.haxis_vmin) / self.haxis_step)) + nystep = int(round((self.vaxis_vmax - self.vaxis_vmin) / self.vaxis_step)) + + # Calculate chart space + xmin = h + self.SPAN + x + xstep = (rectangle.width() - xmin - (h + self.SPAN)) / nxstep + xmax = xmin + xstep * nxstep + + # Calculate Y steps + ymin = h + self.SPAN + ystep = (rectangle.height() - ymin - (h * 2 + self.SPAN)) / nystep + ymax = ymin + ystep * nystep + + painter.save() + painter.translate(rectangle.x(), rectangle.y()) + + # Draw Axis Titles + painter.save() + self._drawAxisTitles(painter, xmin, xmax, ymin, ymax) + painter.restore() + + # Draw Axis Labels + painter.save() + self._drawAxisLabels(painter, xmin, xmax, ymin, ymax, xstep, nxstep, ystep, nystep) + painter.restore() + + # Draw Data + painter.save() + painter.setClipRect(xmin + 1, ymin, xmax - xmin - 2, ymax - ymin - 1) + self._drawData(painter, xmin, xmax, ymin, ymax) + painter.restore() + + # Draw Border + painter.setPen(Qt.black) + painter.drawLine(xmin, ymin, xmin, ymax) + painter.drawLine(xmin, ymax, xmax, ymax) + + painter.restore() + + def _drawAxisTitles(self, painter, xmin, xmax, ymin, ymax): + font_metrics = painter.fontMetrics() + h = font_metrics.xHeight() * 2 + hspan = self.SPAN / 2 + + font = painter.font() + font.setItalic(True) + painter.setFont(font) + + if self.haxis_title is not None: + painter.drawText(xmin + ((xmax - xmin) / 2 - font_metrics.width(self.haxis_title) / 2), ymax + h * 2 + hspan, self.haxis_title) + + if self.vaxis_title is not None: + painter.rotate(90) + painter.drawText(ymin + (ymax - ymin) / 2 - font_metrics.width(self.vaxis_title) / 2, -hspan, self.vaxis_title) + + def _drawAxisLabels(self, painter, xmin, xmax, ymin, ymax, xstep, nxstep, ystep, nystep): + font_metrics = painter.fontMetrics() + h = font_metrics.xHeight() * 2 + + # Draw Internal Grid + painter.setPen(Qt.lightGray) + x = xmin + xstep + ys = ymin if self.haxis_grid else ymax - 4 + for _ in xrange(nxstep): + painter.drawLine(x, ys, x, ymax) + x += xstep + + y = ymin + xe = xmax if self.vaxis_grid else xmin + 4 + for _ in xrange(nystep): + painter.drawLine(xmin, y, xe, y) + y += ystep + + # Draw Axis Labels + painter.setPen(Qt.black) + for i in xrange(1 + nxstep): + x = xmin + (i * xstep) + v = self._hToString(self.haxis_vmin + i * self.haxis_step) + painter.drawText(x - font_metrics.width(v) / 2, 2 + ymax + h, v) + + for i in xrange(1 + nystep): + y = ymin + (i * ystep) + v = self._vToString(self.vaxis_vmin + (nystep - i) * self.vaxis_step) + painter.drawText(xmin - font_metrics.width(v) - 2, y, v) + + def _drawData(self, painter, xmin, xmax, ymin, ymax): + c = 0 + color = self._icolors() + while c < len(self.data.columns): + if c != self._ref_col: + if self._ref_isv: + a, b = c, self._ref_col + else: + a, b = self._ref_col, c + self._drawColumnData(painter, next(color), a, b, xmin, xmax, ymin, ymax) + c += 1 + + def _drawColumnData(self, painter, color, xcol, ycol, xmin, xmax, ymin, ymax): + painter.setPen(QPen(QColor(color), 7, Qt.SolidLine, Qt.RoundCap)) + for row in self.data.rows: + x, y = self._xyFromData(row[xcol], row[ycol], xmin, xmax, ymin, ymax) + painter.drawPoint(x, y) + + def _xyFromData(self, xdata, ydata, xmin, xmax, ymin, ymax): + x = xmin + (float(xdata - self.haxis_vmin) / (self.haxis_vmax - self.haxis_vmin)) * (xmax - xmin) + y = ymin + (1.0 - (float(ydata - self.vaxis_vmin) / (self.vaxis_vmax - self.vaxis_vmin))) * (ymax - ymin) + return x, y + + def _vToString(self, value): + if isinstance(self.vaxis_step, float): + return '%.2f' % value + if isinstance(self.vaxis_step, int): + return '%d' % value + return '%s' % value + + def _hToString(self, value): + if isinstance(self.haxis_step, float): + return '%.2f' % value + if isinstance(self.haxis_step, int): + return '%d' % value + return '%s' % value + + def _setupDefaultValues(self): + def _minMaxDelta(col): + vmin = None + vmax = None + vdelta = 0 + ndelta = 1 + + last_value = self.data.rows[0][col] + vmin = vmax = last_value + for row in self.data.rows[1:]: + vdelta += abs(row[col] - last_value) + ndelta += 1 + if row[col] > vmax: + vmax = row[col] + elif row[col] < vmin: + vmin = row[col] + + return vmin, vmax, vdelta / ndelta + + ref_min, ref_max, ref_step = _minMaxDelta(self._ref_col) + oth_min = oth_max = oth_step = None + for col in xrange(len(self.data.columns)): + if col == self._ref_col: + continue + + cmin, cmax, cstep = _minMaxDelta(col) + oth_min = cmin if oth_min is None else min(cmin, oth_min) + oth_max = cmax if oth_max is None else max(cmax, oth_max) + oth_step = cstep if oth_step is None else (oth_step + cstep) / 2 + + if self._ref_isv: + if self.vaxis_vmin is None: self.vaxis_vmin = ref_min + if self.vaxis_vmax is None: self.vaxis_vmax = ref_max + if self.vaxis_step is None: self.vaxis_step = ref_step + if self.haxis_vmin is None: self.haxis_vmin = oth_min + if self.haxis_vmax is None: self.haxis_vmax = oth_max + if self.haxis_step is None: self.haxis_step = oth_step + else: + if self.haxis_vmin is None: self.haxis_vmin = ref_min + if self.haxis_vmax is None: self.haxis_vmax = ref_max + if self.haxis_step is None: self.haxis_step = ref_step + if self.vaxis_vmin is None: self.vaxis_vmin = oth_min + if self.vaxis_vmax is None: self.vaxis_vmax = oth_max + if self.vaxis_step is None: self.vaxis_step = oth_step + +class LineChart(ScatterChart): + def _drawColumnData(self, painter, color, xcol, ycol, xmin, xmax, ymin, ymax): + painter.setPen(QPen(QColor(color), 2, Qt.SolidLine, Qt.RoundCap)) + + path = QPainterPath() + + row = self.data.rows[0] + x, y = self._xyFromData(row[xcol], row[ycol], xmin, xmax, ymin, ymax) + path.moveTo(x, y) + + for row in self.data.rows[1:]: + x, y = self._xyFromData(row[xcol], row[ycol], xmin, xmax, ymin, ymax) + path.lineTo(x, y) + painter.drawPath(path) + +class AreaChart(ScatterChart): + def _drawColumnData(self, painter, color, xcol, ycol, xmin, xmax, ymin, ymax): + painter.setPen(QPen(QColor(color), 2, Qt.SolidLine, Qt.RoundCap)) + + color = QColor(color) + color.setAlpha(40) + painter.setBrush(color) + + path = QPainterPath() + path.moveTo(xmin, ymax) + for row in self.data.rows: + x, y = self._xyFromData(row[xcol], row[ycol], xmin, xmax, ymin, ymax) + path.lineTo(x, y) + path.lineTo(xmax, ymax) + path.moveTo(xmin, ymax) + painter.drawPath(path) + +class Viewer(QWidget): + def __init__(self): + QWidget.__init__(self) + self.graph = None + + def setGraph(self, func): + self.graph = func + self.update() + + def paintEvent(self, event): + painter = QPainter(self) + painter.setRenderHint(QPainter.Antialiasing) + + if self.graph is not None: + self.graph.draw(painter, QRect(0, 0, event.rect().width() - 120, event.rect().height())) + self.graph.drawLegend(painter, QRect(event.rect().width() - 120, 20, 120, event.rect().height() - 20)) + + painter.end() + +class DialogViewer(QDialog): + def __init__(self): + QDialog.__init__(self) + self.viewer = Viewer() + self.setLayout(QVBoxLayout()) + self.layout().setContentsMargins(0, 0, 0, 0) + self.layout().addWidget(self.viewer) + + def setGraph(self, func): + self.viewer.setGraph(func) + +class DataTable(object): + def __init__(self): + self.columns = [] + self.rows = [] + + def addColumn(self, label): + self.columns.append(label) + + def addRow(self, row): + assert len(row) == len(self.columns) + self.rows.append(row) + +def _pieChartDemo(): + table = DataTable() + table.addColumn('Lang') + table.addColumn('Rating') + table.addRow(['Java', 17.874]) + table.addRow(['C', 17.322]) + table.addRow(['C++', 8.084]) + table.addRow(['C#', 7.319]) + table.addRow(['PHP', 6.096]) + + chart = PieChart(table) + #chart.save('pie.png', QSize(240, 240), 100) + + view = DialogViewer() + view.setGraph(chart) + view.resize(360, 240) + view.exec_() + +def _scatterChartDemo(): + table = DataTable() + table.addColumn('Quality') + table.addColumn('Test 1') + table.addColumn('Test 2') + table.addRow([ 92, 4.9, 8.0]) + table.addRow([ 94, 2.0, 2.5]) + table.addRow([ 96, 7.2, 6.9]) + table.addRow([ 98, 3.5, 1.2]) + table.addRow([100, 8.0, 5.3]) + table.addRow([102, 15.0, 14.2]) + + chart = ScatterChart(table) + chart.haxis_title = 'Process input' + chart.haxis_vmin = 0 + chart.haxis_vmax = 16 + chart.haxis_step = 2 + chart.vaxis_title = 'Quality' + chart.vaxis_vmin = 90 + chart.vaxis_vmax = 104 + chart.vaxis_step = 1 + + #chart.save('scatter.png', QSize(400, 240), 100) + + view = DialogViewer() + view.setGraph(chart) + view.resize(400, 240) + view.exec_() + +def _lineChartDemo(): + table = DataTable() + table.addColumn('Time') + table.addColumn('Site 1') + table.addColumn('Site 2') + table.addColumn('Site 3') + table.addRow([ 4.00, 120, 80, 400]) + table.addRow([ 6.00, 270, 850, 320]) + table.addRow([ 8.30, 50, 1200, 280]) + table.addRow([10.15, 320, 1520, 510]) + table.addRow([12.00, 150, 930, 1100]) + table.addRow([18.20, 62, 1100, 240]) + + chart = LineChart(table) + chart.setHorizontalAxisColumn(0) + chart.haxis_title = 'Time' + chart.haxis_vmin = 0.0 + chart.haxis_vmax = 20.0 + chart.haxis_step = 2 + + #chart.save('line.png', QSize(400, 240), 100) + + view = DialogViewer() + view.setGraph(chart) + view.resize(400, 240) + view.exec_() + +def _areaChartDemo(): + table = DataTable() + table.addColumn('Time') + table.addColumn('Site 1') + table.addColumn('Site 2') + table.addRow([ 4.00, 120, 500]) + table.addRow([ 6.00, 270, 460]) + table.addRow([ 8.30, 1260, 1120]) + table.addRow([10.15, 2030, 540]) + table.addRow([12.00, 520, 890]) + table.addRow([18.20, 1862, 1500]) + + chart = AreaChart(table) + chart.setHorizontalAxisColumn(0) + chart.haxis_title = 'Time' + chart.haxis_vmin = 0.0 + chart.haxis_vmax = 20.0 + chart.haxis_step = 5 + + #chart.save('area.png', QSize(400, 240), 100) + + view = DialogViewer() + view.setGraph(chart) + view.resize(400, 240) + view.exec_() + +if __name__ == '__main__': + import sys + app = QApplication(sys.argv) + + _pieChartDemo() + _scatterChartDemo() + _lineChartDemo() + _areaChartDemo() diff --git a/python/chart.pyc b/python/chart.pyc new file mode 100644 index 0000000..6759715 Binary files /dev/null and b/python/chart.pyc differ diff --git a/python/core b/python/core new file mode 100644 index 0000000..ca453ea Binary files /dev/null and b/python/core differ diff --git a/python/max_freq.py b/python/max_freq.py index be79306..7538161 100644 --- a/python/max_freq.py +++ b/python/max_freq.py @@ -21,7 +21,7 @@ import numpy as np from gnuradio import gr -import code +import code,math,pmt class max_freq(gr.sync_block): """ @@ -36,27 +36,22 @@ class max_freq(gr.sync_block): self.num_decoders=num_decoders self.center_freq=center_freq self.samp_rate=samp_rate - self.num_averages=5 - self.avg_counter=-1 - self.numbers_avg=[] - - + self.snapto=1e5 #100k + self.message_port_register_out(pmt.intern('out')) + def set_center_freq(self, freq=None): + if freq is not None: + if isinstance(freq, float) or isinstance(freq, int): + self.center_freq=freq + else: + self.center_freq = int(freq) def work(self, input_items, output_items): #in0 = input_items[0] #ii=input_items - numbers=abs(input_items[0][0]) - threshold=6 - if self.avg_counter == -1: #init - self.numbers_avg=numbers - self.avg_counter=0 - elif self.avg_counter <= self.num_averages: - #np.mean( np.array([ old_set, new_set ]), axis=0 ) - self.numbers_avg=np.mean( np.array([ self.numbers_avg, numbers ]), axis=0 ) - self.avg_counter+=1 - elif len(np.where(self.numbers_avg>threshold)[0]) >0: - self.avg_counter=0 - numbers=self.numbers_avg - min_consec_max_threshold=4#minimum number of consecutive maximums (in fft domain) to consider signal as station + carrier_width=2 + carrier=self.fft_len/2 + numbers=np.delete(input_items[0][0],range(carrier-carrier_width,carrier+carrier_width+1))#reads input and disregards center + threshold=100 + min_consec_max_threshold=1#minimum number of consecutive maximums (in fft domain) to consider signal as station #TODO: what if no numbers over threshold? #TODO auto threshold #max_indices=[[421, 428, 429, 430, 431, 432, 433, 434, 436, 437, 438, 831, 832, 837, 840, 841, 842, 843, 844, 845, 846, 847, 848, 849, 850, 851,852, 853, 854, 855, 856, 857]] @@ -67,9 +62,14 @@ class max_freq(gr.sync_block): #last_index=0 count=1#counts number of consecutive maximums threshold_reached=False - fuzzyness=10 + fuzzyness=2 # max_indices[0].append(0)#to detect last station max_indices=np.append(max_indices,0)#to detect last station + #try: + + #max_indices.remove(self.fft_len/2)#suppress local oscillator of hackrf + #except ValueError: + #pass for i in max_indices: if abs(i-last_index) <= fuzzyness: count+=i-last_index @@ -89,31 +89,14 @@ class max_freq(gr.sync_block): for index in station_indices: startfreq=self.center_freq-self.samp_rate/2 freq=self.samp_rate*index/self.fft_len+startfreq - station_freqs.append(freq) - - """ -[422 423 426 427 428 430 431 432 433 434 435 436 437 836 837 838 842 843 - 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 861 862 - 0] -[] -[] -[423 424 425 426 427 428 429 430 431 432 433 434 842 843 844 845 848 849 - 850 851 852 853 854 855 858 859 860 0] -[428, 851] -[101303125.0, 102294531.0] -[415 416 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 - 844 845 846 847 848 849 850 851 852 853 854 855 856 861 862 863 0] -[853] -[102299218.0] -""" - #f=open("/tmp/obj","r") - #import pickle - #pickle.load(ii,f) - #(array([431, 433, 437, 439, 849, 854, 856, 858, 861, 862]),) - #code.interact(local=locals()) - # <+signal processing here+> + num_decimals=int(round(math.log(self.snapto,10))) + station_freqs.append(round(freq,-num_decimals)) + for i in range(0,min(self.num_decoders,len(station_freqs))): + msg_string=str(i+1)+" "+str(station_freqs[i]) + send_pmt = pmt.string_to_symbol(msg_string) + self.message_port_pub(pmt.intern('out'), send_pmt) print(max_indices) print(station_indices) print(station_freqs) - return len(input_items[0]) + return len(input_items[0]) diff --git a/python/pylab_piechart b/python/pylab_piechart new file mode 100644 index 0000000..ac94618 --- /dev/null +++ b/python/pylab_piechart @@ -0,0 +1,16 @@ +#from pylab import * +import pylab + +# make a square figure and axes +pylab.figure(1, figsize=(10, 3)) +ax = axes([0.1, 0.1, 0.8, 0.8]) + +labels = 'Frogs', 'Hogs', 'Dogs', 'Logs' +fracs = [15,30,45, 10] + +explode=(0, 0.05, 0, 0) +pylab.pie(fracs, explode=explode, labels=labels, autopct='%1.1f%%', shadow=True) +pylab.title('Raining Hogs and Dogs', bbox={'facecolor':'0.8', 'pad':5}) + +pylab.gca().set_aspect('1') +pylab.show() \ No newline at end of file diff --git a/python/rds_parser_table_qt.py b/python/rds_parser_table_qt.py index 4addc70..282e403 100644 --- a/python/rds_parser_table_qt.py +++ b/python/rds_parser_table_qt.py @@ -21,7 +21,8 @@ from __future__ import print_function#print without newline print('.', end="") import numpy from gnuradio import gr -import pmt,functools,csv,md5 +import pmt,functools,csv,md5,collections +import chart#local file from PyQt4 import Qt, QtCore, QtGui import pprint,code#for easier testing pp = pprint.PrettyPrinter() @@ -46,15 +47,16 @@ class rds_parser_table_qt(gr.sync_block): for i in range(0,nPorts): self.message_port_register_in(pmt.intern('in%d'%i)) self.set_msg_handler(pmt.intern('in%d'%i), functools.partial(self.handle_msg, port=i)) - + self.message_port_register_in(pmt.intern('freq')) + self.set_msg_handler(pmt.intern('freq'), self.set_freq) self.signals=signals self.RDS_data={} self.printcounter=0 self.ODA_application_names={} self.TMC_data={} self.colorder=['ID','freq','name','PTY','AF','time','text','quality','buttons'] - #workdir="/user/wire2/richter/hackrf_prototypes/" - workdir="/media/clemens/intdaten/uni_bulk/forschungsarbeit/hackrf_prototypes/" + workdir="/user/wire2/richter/hackrf_prototypes/" + #workdir="/media/clemens/intdaten/uni_bulk/forschungsarbeit/hackrf_prototypes/" reader = csv.reader(open(workdir+'RDS_ODA AIDs_names_only.csv'), delimiter=',', quotechar='"') reader.next()#skip header for row in reader: @@ -77,6 +79,18 @@ class rds_parser_table_qt(gr.sync_block): reader.next()#skip header self.pty_dict=dict((int(rows[0]),rows[1]) for rows in reader) f.close() + def set_freq(self,msg): + m = pmt.symbol_to_string(msg) + tgtnum=int(m.split()[0])-1#msgs are 1-indexed + freq_str=m.split()[1] + try: + freq=float(freq_str) + freq_str="%0.1fM"% (freq/1e6) + except ValueError: + pass#leave string as is + self.signals.DataUpdateEvent.emit({'row':tgtnum,'freq':freq_str}) + #print("nr:%i freq:%s"%(tgtnum,freq_str)) + def handle_msg(self, msg, port): #code.interact(local=locals()) array=pmt.to_python(msg)[1] @@ -103,6 +117,7 @@ class rds_parser_table_qt(gr.sync_block): self.RDS_data[PI]["AF"]={} self.RDS_data[PI]["DI"]=[2,2,2,2] self.RDS_data[PI]["last_item_toggle_bit"]=2 + self.RDS_data[PI]["internals"]={"last_rt_tooltip":""} print("found station %s"%PI) self.RDS_data[PI]["blockcounts"]["any"]+=1 if self.RDS_data[PI]["blockcounts"]["any"]==5: @@ -321,30 +336,44 @@ class rds_parser_table_qt(gr.sync_block): tag2_len=int(tag2&(2**5-1)) if not self.RDS_data[PI].has_key("RT+"): self.RDS_data[PI]["RT+"]={} - if(self.RDS_data[PI].has_key("RT") and self.RDS_data[PI]["RT_all_valid"]):#TODO better (more fine grained) detection of valid RT+ info + if self.RDS_data[PI].has_key("RT"): rt=self.RDS_data[PI]["RT"] - if not tag1_type=="DUMMY_CLASS": + rt_valid=self.RDS_data[PI]["RT_valid"] + if not tag1_type=="DUMMY_CLASS" and all(rt_valid[tag1_start:tag1_start+tag1_len+1]): self.RDS_data[PI]["RT+"][tag1_type]=rt[tag1_start:tag1_start+tag1_len+1] - if not tag2_type=="DUMMY_CLASS": + if not tag2_type=="DUMMY_CLASS" and all(rt_valid[tag2_start:tag2_start+tag2_len+1]): self.RDS_data[PI]["RT+"][tag2_type]=rt[tag2_start:tag2_start+tag2_len+1] - - if(tag1_type=="ITEM.ARTIST"and tag2_type=="ITEM.TITLE" and self.RDS_data[PI].has_key("RT") and self.RDS_data[PI]["RT_all_valid"]): + tags="ir:%i,it:%i"%(item_running_bit,item_toggle_bit) + afcol=self.colorder.index('AF') + self.signals.DataUpdateEvent.emit({'col':afcol,'row':port,'PI':PI,'string':tags}) + #if(tag1_type=="ITEM.ARTIST"and tag2_type=="ITEM.TITLE" and self.RDS_data[PI].has_key("RT") and self.RDS_data[PI]["RT_all_valid"]): + if(tag2_type=="ITEM.TITLE" and self.RDS_data[PI].has_key("RT")):#TODO remove duplicate code rt=self.RDS_data[PI]["RT"] - artist=rt[tag1_start:tag1_start+tag1_len+1] - song=rt[tag2_start:tag2_start+tag2_len+1] - formatted_text="%s by %s %i"%(song,artist,item_running_bit) + rt_valid=self.RDS_data[PI]["RT_valid"] + artist="?" + song="?" + if all(rt_valid[tag1_start:tag1_start+tag1_len+1]): + artist=rt[tag1_start:tag1_start+tag1_len+1] + if all(rt_valid[tag2_start:tag2_start+tag2_len+1]): + song=rt[tag2_start:tag2_start+tag2_len+1] + formatted_text="%s by %s"%(song,artist) rtcol=self.colorder.index('text') - self.signals.DataUpdateEvent.emit({'col':rtcol,'row':port,'PI':PI,'tooltip':formatted_text}) - #self.signals.DataUpdateEvent.emit({'col':8,'row':port,'PI':PI,'string':formatted_text}) - elif(not tag1_type=="ITEM.ARTIST" and not tag1_type=="DUMMY_CLASS"): - print("%s:RT+: tag1_type:%s, tag2_type:%s"%(PI,tag1_type,tag2_type)) + #only update tooltip if text changed -> remove flicker, still flickers :( + if not formatted_text == self.RDS_data[PI]["internals"]["last_rt_tooltip"]: + self.signals.DataUpdateEvent.emit({'col':rtcol,'row':port,'PI':PI,'tooltip':formatted_text}) + self.RDS_data[PI]["internals"]["last_rt_tooltip"] = formatted_text + #elif(not tag1_type=="ITEM.ARTIST" and not tag1_type=="DUMMY_CLASS"): + # print("%s:RT+: tag1_type:%s, tag2_type:%s"%(PI,tag1_type,tag2_type)) if not self.RDS_data[PI]["last_item_toggle_bit"] == item_toggle_bit: #new item self.RDS_data[PI]["last_item_toggle_bit"] = item_toggle_bit rtcol=self.colorder.index('text') + print("toggle bit changed on PI:%s, cleared RT-tt"%PI) self.signals.DataUpdateEvent.emit({'col':rtcol,'row':port,'PI':PI,'tooltip':""}) - else:#other group - printdelay=50 - self.printcounter+=1 + #else:#other group + if 1==1: + #printdelay=50 + printdelay=500 + self.printcounter+=0#printing disabled if self.RDS_data[PI]["blockcounts"].has_key(groupType): self.RDS_data[PI]["blockcounts"][groupType] +=1 #increment else: @@ -403,9 +432,10 @@ class rds_parser_table_qt(gr.sync_block): formatted_text="%s%s%s"% (textcolor,text[:start],segmentcolor,text[start:end],textcolor,text[end:]) return formatted_text class rds_parser_table_qt_Widget(QtGui.QWidget): - def __init__(self, signals,label): - print("gui initializing") + def __init__(self, signals,label,tableobj): + #print("gui initializing")self.tableobj.RDS_data["D3A2"] self.signals = signals + self.tableobj=tableobj self.signals.DataUpdateEvent.connect(self.display_data) """ Creates the QT Range widget """ QtGui.QWidget.__init__(self) @@ -422,14 +452,12 @@ class rds_parser_table_qt_Widget(QtGui.QWidget): empty_text64='________________________________________________________________' #empty_text64='\xe4'*64 self.data = {'ID':range(1,6), - 'freq':['','','',''], + 'freq':[ QtGui.QLabel() for i in range(4)], 'name':[ QtGui.QLabel() for i in range(4)], 'PTY':[ QtGui.QLabel() for i in range(4)], - #'flags':[ QtGui.QLabel() for i in range(4)], - 'AF':['','','',''], + 'AF':[ QtGui.QLabel() for i in range(4)], 'time':[ QtGui.QLabel() for i in range(4)], 'text':[ QtGui.QLabel("_"*64) for i in range(4)], - #'RT+':[ QtGui.QLabel() for i in range(4)], 'quality':[ QtGui.QLabel() for i in range(4)], 'buttons':[]} #Enter data onto Table @@ -448,9 +476,10 @@ class rds_parser_table_qt_Widget(QtGui.QWidget): newitem = QtGui.QTableWidgetItem(item) self.table.setItem(m, n, newitem) for i in range(0,4):#create buttons - button=QtGui.QPushButton("play") + button=QtGui.QPushButton("getDetails") self.table.setCellWidget(i,self.table.columnCount()-1,button) - button.clicked.connect(self.onCLick) + button.clicked.connect(functools.partial(self.getDetails, row=i)) + #button.clicked.connect(self.getDetails) #Add Header layout.addWidget(self.label) layout.addWidget(self.table) @@ -464,8 +493,9 @@ class rds_parser_table_qt_Widget(QtGui.QWidget): self.event_filter=QtGui.QLineEdit()#QPlainTextEdit ? self.location_filter=QtGui.QLineEdit() - self.button = QtGui.QPushButton("i am a button") - layout.addWidget(self.button) + button = QtGui.QPushButton("code.interact") + button.clicked.connect(self.onCLick) + layout.addWidget(button) filter_layout = Qt.QHBoxLayout() filter_layout.addWidget(QtGui.QLabel("event filter:")) @@ -487,9 +517,14 @@ class rds_parser_table_qt_Widget(QtGui.QWidget): def display_data(self, event): #pp.pprint(event) + if type(event)==dict and event.has_key('TMC_log'): self.logOutput.append(Qt.QString.fromUtf8(event['TMC_log'])) if type(event)==dict and event.has_key('row'): + if event.has_key('freq'): + freqcol=self.colorder.index('freq') + item=self.table.cellWidget(event['row'],freqcol) + item.setText(event['freq']) if event.has_key('wrong_blocks'): item=self.table.cellWidget(event['row'],self.colorder.index('quality')) quality_string="%i%% %s"% (100-2*event['wrong_blocks'],event['dots']) @@ -511,8 +546,9 @@ class rds_parser_table_qt_Widget(QtGui.QWidget): PIcol=self.colorder.index('ID') #rtpcol=self.colorder.index('RT+') rtcol=self.colorder.index('text') - if not self.table.item(event['row'],PIcol).text() == event['PI']: + if not str(self.table.item(event['row'],PIcol).text()) == event['PI']: #self.table.cellWidget(event['row'],rtpcol).setText("")#clear RT+ on changed PI + print("PI changed on row %i, cleared RT-tt"%event['row']) self.table.cellWidget(event['row'],rtcol).setToolTip(Qt.QString("")) self.table.item(event['row'],PIcol).setText(event['PI']) if event.has_key('AF'): @@ -525,6 +561,36 @@ class rds_parser_table_qt_Widget(QtGui.QWidget): item=self.table.cellWidget(event['row'],PSNcol) item.setText(event['PSN']) self.table.resizeColumnsToContents() + def getDetails(self,row): + PIcol=self.colorder.index('ID') + PI=str(self.table.item(row,PIcol).text()) + #PI= + #print("row:%i,PI:%s"%(row,PI)) + #print(self.tableobj.RDS_data[PI]) + table=chart.DataTable() + table.addColumn('groupType') + table.addColumn('numPackets') + #ordered_blockcounts=self.tableobj.RDS_data["D00F"]['blockcounts'] + blockcounts=self.tableobj.RDS_data[PI]['blockcounts'].copy() + del blockcounts['any'] + #lambda function removes last character of PI string (A or B) and sorts based on integer valure of number in front + for key in sorted(blockcounts,key=lambda elem: int(elem[0:-1])): + count=blockcounts[key] + table.addRow([key+": "+str(count),count]) + mychart=chart.PieChart(table) + view = chart.DialogViewer() + view.setGraph(mychart) + #view.resize(360, 240) + view.resize(330, 420) + rds_data=self.tableobj.RDS_data[PI].copy() + del rds_data['blockcounts'] + del rds_data['PSN_valid'] + del rds_data['RT_valid'] + l=QtGui.QLabel("Data:%s"%str(rds_data)) + l.setWordWrap(True) + #l=QtGui.QLabel("Data:") + view.layout().addWidget(l) + view.exec_() def onCLick(self): print("button clicked") code.interact(local=locals()) diff --git a/python/smooth_vectors.py b/python/smooth_vectors.py new file mode 100644 index 0000000..753d1d6 --- /dev/null +++ b/python/smooth_vectors.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2016 <+YOU OR YOUR COMPANY+>. +# +# This is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# This software is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this software; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +import numpy as np +from gnuradio import gr + +class smooth_vectors(gr.decim_block): + """ + docstring for block smooth_vectors + """ + def __init__(self, vec_len,decim,moving_avg_len): + gr.decim_block.__init__(self, + name="smooth_vectors", + in_sig=[(np.float32,vec_len)], + out_sig=[(np.float32,vec_len)], decim=decim) + self.vec_len=vec_len + self.decim=decim + self.moving_avg_len=moving_avg_len + self.last_inputs=[] + self.count=0 + + + def work(self, input_items, output_items): + in0 = input_items[0] + out = output_items[0] + self.last_inputs.insert(0,in0) + out[:] =np.mean( np.array(self.last_inputs), axis=0 ) + # <+signal processing here+> + if len(self.last_inputs)>self.moving_avg_len: + self.last_inputs.pop(len(self.last_inputs)-1)#remove last + #out[:] = in0 + return len(output_items[0]) +