diff --git a/grc/CMakeLists.txt b/grc/CMakeLists.txt
index 6fb7c98..c635b93 100644
--- a/grc/CMakeLists.txt
+++ b/grc/CMakeLists.txt
@@ -16,10 +16,10 @@
# along with GNU Radio; see the file COPYING. If not, write to
# the Free Software Foundation, Inc., 51 Franklin Street,
# Boston, MA 02110-1301, USA.
-
install(FILES
crfa_multi_rds_printer.xml
crfa_qtguitest.xml
crfa_rds_table_qt.xml
- crfa_rds_parser_table_qt.xml DESTINATION share/gnuradio/grc/blocks
+ crfa_rds_parser_table_qt.xml
+ crfa_rds_decoder.xml DESTINATION share/gnuradio/grc/blocks
)
diff --git a/grc/crfa_rds_decoder.xml b/grc/crfa_rds_decoder.xml
new file mode 100644
index 0000000..9bb12c9
--- /dev/null
+++ b/grc/crfa_rds_decoder.xml
@@ -0,0 +1,45 @@
+
+
+ RDS Decoder (cr)
+ crfa_rds_decoder
+ [crfa]
+ import crfa
+ crfa.rds_decoder($log, $debug)
+
+ Log
+ log
+ False
+ bool
+
+
+
+
+ Debug
+ debug
+ False
+ bool
+
+
+
+
+ in
+ byte
+
+
+ out
+ message
+ 1
+
+
diff --git a/grc/rds_decoder.xml b/grc/rds_decoder.xml
new file mode 100644
index 0000000..3d6384b
--- /dev/null
+++ b/grc/rds_decoder.xml
@@ -0,0 +1,45 @@
+
+
+ RDS Decoder (cr)
+ gr_rds_decoder_cr
+ [RDS]
+ import rds
+ rds.decoder($log, $debug)
+
+ Log
+ log
+ False
+ bool
+
+
+
+
+ Debug
+ debug
+ False
+ bool
+
+
+
+
+ in
+ byte
+
+
+ out
+ message
+ 1
+
+
diff --git a/include/crfa/CMakeLists.txt b/include/crfa/CMakeLists.txt
index 3261a5f..5743aa6 100644
--- a/include/crfa/CMakeLists.txt
+++ b/include/crfa/CMakeLists.txt
@@ -22,5 +22,5 @@
########################################################################
install(FILES
api.h
- DESTINATION include/crfa
+ rds_decoder.h DESTINATION include/crfa
)
diff --git a/include/crfa/rds_decoder.h b/include/crfa/rds_decoder.h
new file mode 100644
index 0000000..7557e65
--- /dev/null
+++ b/include/crfa/rds_decoder.h
@@ -0,0 +1,56 @@
+/* -*- c++ -*- */
+/*
+ * 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.
+ */
+
+
+#ifndef INCLUDED_CRFA_RDS_DECODER_H
+#define INCLUDED_CRFA_RDS_DECODER_H
+
+#include
+#include
+
+namespace gr {
+ namespace crfa {
+
+ /*!
+ * \brief <+description of block+>
+ * \ingroup crfa
+ *
+ */
+ class CRFA_API rds_decoder : virtual public gr::sync_block
+ {
+ public:
+ typedef boost::shared_ptr sptr;
+
+ /*!
+ * \brief Return a shared_ptr to a new instance of crfa::rds_decoder.
+ *
+ * To avoid accidental use of raw pointers, crfa::rds_decoder's
+ * constructor is in a private implementation
+ * class. crfa::rds_decoder::make is the public interface for
+ * creating new instances.
+ */
+ static sptr make(bool log, bool debug);
+ };
+
+ } // namespace crfa
+} // namespace gr
+
+#endif /* INCLUDED_CRFA_RDS_DECODER_H */
+
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
index 21eb294..385b6c2 100644
--- a/lib/CMakeLists.txt
+++ b/lib/CMakeLists.txt
@@ -24,9 +24,8 @@ include(GrPlatform) #define LIB_SUFFIX
include_directories(${Boost_INCLUDE_DIR})
link_directories(${Boost_LIBRARY_DIRS})
-
list(APPEND crfa_sources
-)
+ rds_decoder_impl.cc )
set(crfa_sources "${crfa_sources}" PARENT_SCOPE)
if(NOT crfa_sources)
diff --git a/lib/constants.h b/lib/constants.h
new file mode 100644
index 0000000..cb1f85c
--- /dev/null
+++ b/lib/constants.h
@@ -0,0 +1,201 @@
+/*
+ * Copyright 2004 Free Software Foundation, Inc.
+ *
+ * This file is part of GNU Radio
+ *
+ * GNU Radio 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 2, or (at your option)
+ * any later version.
+ *
+ * GNU Radio 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 GNU Radio; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+
+/* see page 59, Annex C, table C.1 in the standard
+ * offset word C' has been put at the end */
+static const unsigned int offset_pos[5]={0,1,2,3,2};
+static const unsigned int offset_word[5]={252,408,360,436,848};
+static const unsigned int syndrome[5]={383,14,303,663,748};
+static const char * const offset_name[]={"A","B","C","D","C'"};
+
+/* Annex F of RBDS Standard Table F.1 (North America) and
+ * Table F.2 (Europe) */
+const std::string pty_table[32][2]={
+ {"Undefined", "Undefined"},
+ {"News", "News"},
+ {"Current Affairs", "Information"},
+ {"Information", "Sports"},
+ {"Sport", "Talk"},
+ {"Education", "Rock"},
+ {"Drama", "Classic Rock"},
+ {"Culture", "Adult Hits"},
+ {"Science", "Soft Rock"},
+ {"Varied", "Top 40"},
+ {"Pop Music", "Country"},
+ {"Rock Music", "Oldies"},
+ {"Easy Listening", "Soft"},
+ {"Light Classical", "Nostalgia"},
+ {"Serious Classical", "Jazz"},
+ {"Other Music", "Classical"},
+ {"Weather", "Rhythm & Blues"},
+ {"Finance", "Soft Rhythm & Blues"},
+ {"Children’s Programmes", "Language"},
+ {"Social Affairs", "Religious Music"},
+ {"Religion", "Religious Talk"},
+ {"Phone-In", "Personality"},
+ {"Travel", "Public"},
+ {"Leisure", "College"},
+ {"Jazz Music", "Spanish Talk"},
+ {"Country Music", "Spanish Music"},
+ {"National Music", "Hip Hop"},
+ {"Oldies Music", "Unassigned"},
+ {"Folk Music", "Unassigned"},
+ {"Documentary", "Weather"},
+ {"Alarm Test", "Emergency Test"},
+ {"Alarm", "Emergency"}};
+
+/* page 71, Annex D, table D.1 in the standard */
+const std::string pi_country_codes[15][5]={
+ {"DE","GR","MA","__","MD"},
+ {"DZ","CY","CZ","IE","EE"},
+ {"AD","SM","PL","TR","__"},
+ {"IL","CH","VA","MK","__"},
+ {"IT","JO","SK","__","__"},
+ {"BE","FI","SY","__","UA"},
+ {"RU","LU","TN","__","__"},
+ {"PS","BG","__","NL","PT"},
+ {"AL","DK","LI","LV","SI"},
+ {"AT","GI","IS","LB","__"},
+ {"HU","IQ","MC","__","__"},
+ {"MT","GB","LT","HR","__"},
+ {"DE","LY","YU","__","__"},
+ {"__","RO","ES","SE","__"},
+ {"EG","FR","NO","BY","BA"}};
+
+/* page 72, Annex D, table D.2 in the standard */
+const std::string coverage_area_codes[16]={
+ "Local",
+ "International",
+ "National",
+ "Supra-regional",
+ "Regional 1",
+ "Regional 2",
+ "Regional 3",
+ "Regional 4",
+ "Regional 5",
+ "Regional 6",
+ "Regional 7",
+ "Regional 8",
+ "Regional 9",
+ "Regional 10",
+ "Regional 11",
+ "Regional 12"};
+
+const std::string rds_group_acronyms[16]={
+ "BASIC",
+ "PIN/SL",
+ "RT",
+ "AID",
+ "CT",
+ "TDC",
+ "IH",
+ "RP",
+ "TMC",
+ "EWS",
+ "___",
+ "___",
+ "___",
+ "___",
+ "EON",
+ "___"};
+
+/* page 74, Annex E, table E.1 in the standard: that's the ASCII table!!! */
+
+/* see page 84, Annex J in the standard */
+const std::string language_codes[44]={
+ "Unkown/not applicable",
+ "Albanian",
+ "Breton",
+ "Catalan",
+ "Croatian",
+ "Welsh",
+ "Czech",
+ "Danish",
+ "German",
+ "English",
+ "Spanish",
+ "Esperanto",
+ "Estonian",
+ "Basque",
+ "Faroese",
+ "French",
+ "Frisian",
+ "Irish",
+ "Gaelic",
+ "Galician",
+ "Icelandic",
+ "Italian",
+ "Lappish",
+ "Latin",
+ "Latvian",
+ "Luxembourgian",
+ "Lithuanian",
+ "Hungarian",
+ "Maltese",
+ "Dutch",
+ "Norwegian",
+ "Occitan",
+ "Polish",
+ "Portuguese",
+ "Romanian",
+ "Romansh",
+ "Serbian",
+ "Slovak",
+ "Slovene",
+ "Finnish",
+ "Swedish",
+ "Turkish",
+ "Flemish",
+ "Walloon"};
+
+/* see page 12 in ISO 14819-1 */
+const std::string tmc_duration[8][2]={
+ {"no duration given", "no duration given"},
+ {"15 minutes", "next few hours"},
+ {"30 minutes", "rest of the day"},
+ {"1 hour", "until tomorrow evening"},
+ {"2 hours", "rest of the week"},
+ {"3 hours", "end of next week"},
+ {"4 hours", "end of the month"},
+ {"rest of the day", "long period"}};
+
+/* optional message content, data field lengths and labels
+ * see page 15 in ISO 14819-1 */
+const int optional_content_lengths[16]={3,3,5,5,5,8,8,8,8,11,16,16,16,16,0,0};
+
+const std::string label_descriptions[16]={
+ "Duration",
+ "Control code",
+ "Length of route affected",
+ "Speed limit advice",
+ "Quantifier",
+ "Quantifier",
+ "Supplementary information code",
+ "Explicit start time",
+ "Explicit stop time",
+ "Additional event",
+ "Detailed diversion instructions",
+ "Destination",
+ "RFU (Reserved for future use)",
+ "Cross linkage to source of problem, or another route",
+ "Separator",
+ "RFU (Reserved for future use)"};
diff --git a/lib/rds_decoder_impl.cc b/lib/rds_decoder_impl.cc
new file mode 100644
index 0000000..7bacaa5
--- /dev/null
+++ b/lib/rds_decoder_impl.cc
@@ -0,0 +1,256 @@
+/* -*- c++ -*- */
+/*
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#define dout debug && std::cout
+#define lout log && std::cout
+
+#include
+#include "constants.h"
+#include "rds_decoder_impl.h"
+
+namespace gr {
+ namespace crfa {
+
+ rds_decoder::sptr
+ rds_decoder::make(bool log, bool debug)
+ {
+ return gnuradio::get_initial_sptr
+ (new rds_decoder_impl(log, debug));
+ }
+
+ /*
+ * The private constructor
+ */
+ rds_decoder_impl::rds_decoder_impl(bool log, bool debug)
+ : gr::sync_block("rds_decoder",
+ gr::io_signature::make (1, 1, sizeof(char)),
+ gr::io_signature::make (0, 0, 0)),
+ log(log),
+ debug(debug)
+{
+ set_output_multiple(104); // 1 RDS datagroup = 104 bits
+ message_port_register_out(pmt::mp("out"));
+ enter_no_sync();
+}
+
+ /*
+ * Our virtual destructor.
+ */
+ rds_decoder_impl::~rds_decoder_impl()
+ {
+ }
+
+////////////////////////// HELPER FUNTIONS /////////////////////////
+
+void rds_decoder_impl::enter_no_sync() {
+ presync = false;
+ d_state = NO_SYNC;
+}
+
+void rds_decoder_impl::enter_sync(unsigned int sync_block_number) {
+ last_wrong_blocks_counter = 0;
+ wrong_blocks_counter = 0;
+ blocks_counter = 0;
+ block_bit_counter = 0;
+ block_number = (sync_block_number + 1) % 4;
+ group_assembly_started = false;
+ d_state = SYNC;
+}
+
+/* see Annex B, page 64 of the standard */
+unsigned int rds_decoder_impl::calc_syndrome(unsigned long message,
+ unsigned char mlen) {
+ unsigned long reg = 0;
+ unsigned int i;
+ const unsigned long poly = 0x5B9;
+ const unsigned char plen = 10;
+
+ for (i = mlen; i > 0; i--) {
+ reg = (reg << 1) | ((message >> (i-1)) & 0x01);
+ if (reg & (1 << plen)) reg = reg ^ poly;
+ }
+ for (i = plen; i > 0; i--) {
+ reg = reg << 1;
+ if (reg & (1<> 8U) & 0xffU;
+ bytes[1] = (group[0] ) & 0xffU;
+ bytes[2] = (group[1] >> 8U) & 0xffU;
+ bytes[3] = (group[1] ) & 0xffU;
+ bytes[4] = (group[2] >> 8U) & 0xffU;
+ bytes[5] = (group[2] ) & 0xffU;
+ bytes[6] = (group[3] >> 8U) & 0xffU;
+ bytes[7] = (group[3] ) & 0xffU;
+
+ // RDS offset words
+ bytes[8] = offset_chars[0];
+ bytes[9] = offset_chars[1];
+ bytes[10] = offset_chars[2];
+ bytes[11] = offset_chars[3];
+ bytes[12]=last_wrong_blocks_counter;
+ pmt::pmt_t data(pmt::make_blob(bytes, 13));
+ pmt::pmt_t meta(pmt::PMT_NIL);
+
+ pmt::pmt_t pdu(pmt::cons(meta, data)); // make PDU: (metadata, data) pair
+ message_port_pub(pmt::mp("out"), pdu);
+}
+//work function
+int rds_decoder_impl::work (int noutput_items,
+ gr_vector_const_void_star &input_items,
+ gr_vector_void_star &output_items)
+{
+ const bool *in = (const bool *) input_items[0];
+
+ dout << "RDS data decoder at work: input_items = "
+ << noutput_items << ", /104 = "
+ << noutput_items / 104 << std::endl;
+
+ int i=0,j;
+ unsigned long bit_distance, block_distance;
+ unsigned int block_calculated_crc, block_received_crc, checkword,dataword;
+ unsigned int reg_syndrome;
+ unsigned char offset_char('x'); // x = error while decoding the word offset
+
+/* the synchronization process is described in Annex C, page 66 of the standard */
+ while (i=offset_pos[j])
+ block_distance=offset_pos[j]+4-offset_pos[lastseen_offset];
+ else
+ block_distance=offset_pos[j]-offset_pos[lastseen_offset];
+ if ((block_distance*26)!=bit_distance) presync=false;
+ else {
+ lout << "@@@@@ Sync State Detected" << std::endl;
+ enter_sync(j);
+ }
+ }
+ break; //syndrome found, no more cycles
+ }
+ }
+ break;
+ case SYNC:
+/* wait until 26 bits enter the buffer */
+ if (block_bit_counter<25) block_bit_counter++;
+ else {
+ good_block=false;
+ dataword=(reg>>10) & 0xffff;
+ block_calculated_crc=calc_syndrome(dataword,16);
+ checkword=reg & 0x3ff;
+/* manage special case of C or C' offset word */
+ if (block_number==2) {
+ block_received_crc=checkword^offset_word[block_number];
+ if (block_received_crc==block_calculated_crc) {
+ good_block=true;
+ offset_char = 'C';
+ } else {
+ block_received_crc=checkword^offset_word[4];
+ if (block_received_crc==block_calculated_crc) {
+ good_block=true;
+ offset_char = 'c'; // C' (C-Tag)
+ } else {
+ wrong_blocks_counter++;
+ good_block=false;
+ }
+ }
+ }
+ else {
+ block_received_crc=checkword^offset_word[block_number];
+ if (block_received_crc==block_calculated_crc) {
+ good_block=true;
+ if (block_number==0) offset_char = 'A';
+ else if (block_number==1) offset_char = 'B';
+ else if (block_number==3) offset_char = 'D';
+ } else {
+ wrong_blocks_counter++;
+ good_block=false;
+ }
+ }
+/* done checking CRC */
+ if (block_number==0 && good_block) {
+ group_assembly_started=true;
+ group_good_blocks_counter=1;
+ }
+ if (group_assembly_started) {
+ if (!good_block) group_assembly_started=false;
+ else {
+ group[block_number]=dataword;
+ offset_chars[block_number] = offset_char;
+ group_good_blocks_counter++;
+ }
+ if (group_good_blocks_counter==5) decode_group(group);
+ }
+ block_bit_counter=0;
+ block_number=(block_number+1) % 4;
+ blocks_counter++;
+/* 1187.5 bps / 104 bits = 11.4 groups/sec, or 45.7 blocks/sec */
+ if (blocks_counter==50) {
+ last_wrong_blocks_counter=wrong_blocks_counter;
+ if (wrong_blocks_counter>35) {
+ lout << "@@@@@ Lost Sync (Got " << wrong_blocks_counter
+ << " bad blocks on " << blocks_counter
+ << " total)" << std::endl;
+ enter_no_sync();
+ } else {
+ lout << "@@@@@ Still Sync-ed (Got " << wrong_blocks_counter
+ << " bad blocks on " << blocks_counter
+ << " total)" << std::endl;
+ }
+ blocks_counter=0;
+ wrong_blocks_counter=0;
+ }
+ }
+ break;
+ default:
+ d_state=NO_SYNC;
+ break;
+ }
+ i++;
+ bit_counter++;
+ }
+ return noutput_items;
+ }/*work function*/
+ } /* namespace crfa */
+} /* namespace gr */
+
diff --git a/lib/rds_decoder_impl.h b/lib/rds_decoder_impl.h
new file mode 100644
index 0000000..2fa36a3
--- /dev/null
+++ b/lib/rds_decoder_impl.h
@@ -0,0 +1,70 @@
+/* -*- c++ -*- */
+/*
+ * 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.
+ */
+
+#ifndef INCLUDED_CRFA_RDS_DECODER_IMPL_H
+#define INCLUDED_CRFA_RDS_DECODER_IMPL_H
+
+#include
+
+namespace gr {
+ namespace crfa {
+
+ class rds_decoder_impl : public rds_decoder
+ {
+public:
+ rds_decoder_impl(bool log, bool debug);
+
+private:
+ ~rds_decoder_impl();
+
+ // Where all the action really happens
+ int work(int noutput_items,
+ gr_vector_const_void_star &input_items,
+ gr_vector_void_star &output_items);
+ void enter_no_sync();
+ void enter_sync(unsigned int);
+ unsigned int calc_syndrome(unsigned long, unsigned char);
+ void decode_group(unsigned int*);
+
+ unsigned long bit_counter;
+ unsigned long lastseen_offset_counter, reg;
+ unsigned int block_bit_counter;
+ unsigned int wrong_blocks_counter;
+ unsigned int blocks_counter;
+ unsigned int group_good_blocks_counter;
+ unsigned int group[4];
+ unsigned char offset_chars[4]; // [ABCcDEx] (x=error)
+ bool debug;
+ bool log;
+ bool presync;
+ bool good_block;
+ bool group_assembly_started;
+ unsigned char last_wrong_blocks_counter;
+ unsigned char lastseen_offset;
+ unsigned char block_number;
+ enum { NO_SYNC, SYNC } d_state;
+
+ };
+
+ } // namespace crfa
+} // namespace gr
+
+#endif /* INCLUDED_CRFA_RDS_DECODER_IMPL_H */
+
diff --git a/python/piechart.py b/python/piechart.py
new file mode 100644
index 0000000..fbece54
--- /dev/null
+++ b/python/piechart.py
@@ -0,0 +1,38 @@
+from PyQt4.QtGui import QGraphicsScene, QApplication, QGraphicsView, QGraphicsEllipseItem
+from PyQt4 import Qt, QtCore, QtGui
+from PyQt4.Qt import QColor
+import sys, random,code
+
+
+app = QApplication(sys.argv)
+scene = QGraphicsScene()
+
+families = [1,2,3,4,5,6,7,8,9,10]
+total = 0
+set_angle = 0
+count1 = 0
+colours = []
+total = sum(families)
+size=300
+for count in range(len(families)):
+ number = []
+ for count in range(3):
+ number.append(random.randrange(0, 255))
+ colours.append(QColor(number[0],number[1],number[2]))
+
+for family in families:
+ # Max span is 5760, so we have to calculate corresponding span angle
+ angle = round(float(family*5760)/total)
+ ellipse = QGraphicsEllipseItem(0,0,size,size)
+ ellipse.setPos(0,0)
+ ellipse.setStartAngle(set_angle)
+ ellipse.setSpanAngle(angle)
+ ellipse.setBrush(colours[count1])
+ set_angle += angle
+ count1 += 1
+ scene.addItem(ellipse)
+
+view = QGraphicsView(scene)
+view.show()
+view.setFixedSize(size+10,size+10)
+app.exec_(code.interact(local=locals()))
diff --git a/python/python/rds_parser_table_qt.py b/python/python/rds_parser_table_qt.py
deleted file mode 100644
index b5e0866..0000000
--- a/python/python/rds_parser_table_qt.py
+++ /dev/null
@@ -1,335 +0,0 @@
-#!/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
-from gnuradio import gr
-import code,pmt,functools
-from PyQt4 import Qt, QtCore, QtGui
-import pprint
-pp = pprint.PrettyPrinter()
-
-from PyQt4.QtCore import QObject, pyqtSignal
-
-class rds_parser_table_qt_Signals(QObject):
- DataUpdateEvent = QtCore.pyqtSignal(dict)
- def __init__(self, parent=None):
- super(QtCore.QObject, self).__init__()
-
-class rds_parser_table_qt(gr.sync_block):
- """
- docstring for block qtguitest
- """
- def __init__(self,signals,nPorts):
- #QObject.__init__()
- gr.sync_block.__init__(self,
- name="RDS Table",
- in_sig=None,
- out_sig=None)
- 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.signals=signals
- self.RTdict={}
- self.RTvalid={}
- self.PSNdict={}
- self.PSNvalid={}
- self.AFdata={}
- self.blockcounts={}
- self.printcounter=0
- def handle_msg(self, msg, port):
- #code.interact(local=locals())
- array=pmt.to_python(msg)[1]
- groupNR=array[2]&0b11110000
- groupVar=array[2]&0b00001000
- if (groupVar == 0):
- groupType=str(groupNR >> 4)+"A"
- else:
- groupType=str(groupNR >> 4)+"B"
- #print("raw:"+str(pmt.to_python(msg))+"\n")
- PI="%02X%02X" %(array[0],array[1])
- #print("1st block:"+str(array[0])+","+str(array[1])+"= ID: %s" %PI)
- #print("2st block:"+str(array[2])+","+str(array[3])+"= type:"+groupType)
- #print("3st block:"+str(array[4])+","+str(array[5]))
- #print("4st block:"+str(array[6])+","+str(array[7]))
- if (groupType == "0A"):#AF PSN
- adr=array[3]&0b00000011
- segment=self.decode_chars(chr(array[6])+chr(array[7]))
- if(not self.PSNdict.has_key(PI)):#initialize dict
- self.PSNdict[PI]="_"*8
- self.PSNvalid[PI]=[False]*8
- self.AFdata[PI]={}
- #1110 0000 = no AF
- #1110 0001 = 1AF
- #1111 1001 = 25AF
-
- if(array[5]>= 224 and array[5]<= 249):
- print("AF1 detected")
- self.AFdata[PI]['number']=array[5]-224
- self.signals.DataUpdateEvent.emit({'row':port,'AF':self.AFdata[PI]})
- if(array[6]>= 224 and array[6]<= 249):
- print("AF2 detected")
-
- name_list=list(self.PSNdict[PI])
- if (name_list[adr*2:adr*2+2]==list(segment)):#segment already there
- segmentcolor="green"
- elif(name_list[adr*2:adr*2+2]==['_']*2): #segment new
- segmentcolor="orange"
- name_list[adr*2:adr*2+2]=segment
- else:#name changed (böse)
- segmentcolor="red"
- name_list=['_']*8 #reset name
- name_list[adr*2:adr*2+2]=segment
- #reset stored text:
- self.PSNdict[PI]="_"*8
- self.PSNvalid[PI]=[False]*8
- self.PSNvalid[PI][adr*2:adr*2+2]=[True] *2
- self.PSNdict[PI]="".join(name_list)
- #determine if text is valid
- valid=True
- for i in range(0,8):
- if (not self.PSNvalid[PI][i]):
- valid = False
- if(valid):
- textcolor="black"
- else:
- textcolor="gray"
- formatted_text=self.color_text(self.PSNdict[PI],adr*2,adr*2+2,textcolor,segmentcolor)
- self.signals.DataUpdateEvent.emit({'col':5,'row':port,'PI':PI,'PSN':formatted_text})
- elif (groupType == "2A"):#RT radiotext
- if(not self.RTdict.has_key(PI)):#initialize dict
- self.RTdict[PI]="_"*64
- self.RTvalid[PI]=[False]*64
- else:
- adr=array[3]&0b00001111
- segment=self.decode_chars(chr(array[4])+chr(array[5])+chr(array[6])+chr(array[7]))
- #print("RT:adress: %d, segment:%s"%(adr,segment))
- #self.signals.DataUpdateEvent.emit({'col':5,'row':port,'PI':PI,'groupType':groupType,'adress':adr,'segment':segment})
- text_list=list(self.RTdict[PI])
- #determine text length:
- try:
- text_end=text_list.index('\r')
- except ValueError:
- text_end=64 #assume whole string is important
- pass
-
- if (text_list[adr*4:adr*4+4]==list(segment)):#segment already there
- segmentcolor="green"
- elif (text_list[adr*4:adr*4+4]==['_']*4):#segment new
- segmentcolor="orange"
- text_list[adr*4:adr*4+4]=segment
- else:
- segmentcolor="red"
- text_list=['_']*64 #clear text
- text_list[adr*4:adr*4+4]=segment
- #reset stored text:
- self.RTdict[PI]="_"*64
- self.RTvalid[PI]=[False]*64
-
- self.RTvalid[PI][adr*4:adr*4+4]=[True] *4
- self.RTdict[PI]="".join(text_list)
-
- #determine if (new) text is valid
- valid=True
- for i in range(0,text_end):
- if (not self.RTvalid[PI][i]):
- valid = False
- if(valid):
- textcolor="black"
- else:
- textcolor="gray"
- #formatted_text="%s%s%s"% (textcolor,self.RTdict[PI][:adr*4],segmentcolor,self.RTdict[PI][adr*4:adr*4+4],textcolor,self.RTdict[PI][adr*4+4:])
- formatted_text=self.color_text(self.RTdict[PI],adr*4,adr*4+4,textcolor,segmentcolor)
- #print(self.RTdict[PI]+" valid:"+str(valid)+"valarr:"+str(self.RTvalid[PI]))
-
-
- self.signals.DataUpdateEvent.emit({'col':5,'row':port,'PI':PI,'string':formatted_text})
- #code.interact(local=locals())
- elif (groupType == "4A"):#CT clock time
- datecode=((array[3] & 0x03) << 15) | (array[4] <<7)|((array[5] >> 1) & 0x7f)
- hours=((array[5] & 0x1) << 4) | ((array[6] >> 4) & 0x0f)
- minutes=((array[6] &0x0F)<<2)|((array[7] >>6)&0x3)
- offsetdir=(array[7]>>5)&0x1
- local_time_offset=0.5*((array[7])&0x1F)
- if(offsetdir==1):
- local_time_offset*=-1
- year=int((datecode - 15078.2) / 365.25)
- month=int((datecode - 14956.1 - int(year * 365.25)) / 30.6001)
- day=datecode - 14956 - int(year * 365.25) - int(month * 30.6001)
- if(month == 14 or month == 15):#no idea why -> annex g of RDS spec
- year += 1;
- month -= 13
- year+=1900
- datestring="%02i.%02i.%4i, %02i:%02i (%+.1fh)" % (day,month,year,hours,minutes,local_time_offset)
- self.signals.DataUpdateEvent.emit({'col':4,'row':port,'PI':PI,'string':datestring})
- else:#other group
- printfreq=100
- self.printcounter+=1
- if self.blockcounts.has_key(PI):#1st group on this station
- if self.blockcounts[PI].has_key(groupType):#1st group of this type
- self.blockcounts[PI][groupType] +=1 #increment
- else:
- self.blockcounts[PI][groupType] = 1 #initialize
- else:
- self.blockcounts[PI]={}#initialize dict
- if self.printcounter == printfreq:
- pp.pprint(self.blockcounts)
- self.printcounter=0
- #print("group of type %s not decoded on station %s"% (groupType,PI))
- def decode_chars(self,charstring):
- alphabet={
- 0b1000:u"áàéèíìóòúùÑÇŞßiIJ",
- 0b1001:u"âäêëîïôöûüñçş??ij",
- 0b1100:u"ÁÀÉÈÍÌÓÒÚÙŘČŠŽĐĿ",
- 0b1101:u"áàéèíìóòúùřčšžđŀ"}
- charlist=list(charstring)
- for i,char in enumerate(charstring):
- #code.interact(local=locals())
- if ord(char)<= 0b01111111:
- charlist[i]=char #use ascii
- else:
- #split byte
- alnr=(ord(char)&0xF0 )>>4 #upper 4 bit
- index=ord(char)&0x0F #lower 4 bit
- #code.interact(local=locals())
- try:
- charlist[i]=alphabet[alnr][index]
- except KeyError:
- charlist[i]=char
- pass
- return "".join(charlist)
- def color_text(self, text, start,end,textcolor,segmentcolor):
- 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")
- self.signals = signals
- self.signals.DataUpdateEvent.connect(self.display_data)
- """ Creates the QT Range widget """
- QtGui.QWidget.__init__(self)
- layout = Qt.QVBoxLayout()
- self.label = Qt.QLabel(label)
- layout.addWidget(self.label)
- self.setLayout(layout)
- self.table=QtGui.QTableWidget(self)
- self.table.setRowCount(5)
- self.table.setColumnCount(7)
- self.table.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers) #disallow editing
- #Data
- empty_text32='________________________________'
- empty_text64='________________________________________________________________'
- #empty_text64='\xe4'*64
- self.data = {'ID':range(1,6),
- 'freq':['','','',''],
- 'name':[],
- 'AF':['','','',''],
- 'time':[],
- 'text':[],
- 'buttons':[]}
- #Enter data onto Table
- horHeaders = []
- for n, key in enumerate(['ID','freq','name','AF','time','text','buttons']):
- #for n, key in enumerate(sorted(self.data.keys())):
- horHeaders.append(key)
- for m, item in enumerate(self.data[key]):
- if type(item)==int:#convert ints to strings
- newitem = QtGui.QTableWidgetItem(str(item))
- else:
- newitem = QtGui.QTableWidgetItem(item)
- self.table.setItem(m, n, newitem)
- for i in range(0,4):#create buttons
- button=QtGui.QPushButton("play")
- self.table.setCellWidget(i,self.table.columnCount()-1,button)
- button.clicked.connect(self.onCLick)
- for i in range(0,4):#create text labels
- label=QtGui.QLabel(empty_text64)
- #label.setFont(QtGui.QFont("Courier New"))
- self.table.setCellWidget(i,self.table.columnCount()-2,label)
- for i in range(0,4):#create name labels
- label=QtGui.QLabel("_"*8)
- #label.setFont(QtGui.QFont("Courier New"))
- self.table.setCellWidget(i,2,label)
- for i in range(0,4):#create time labels
- label=QtGui.QLabel()
- #label.setFont(QtGui.QFont("Courier New"))
- self.table.setCellWidget(i,4,label)
- #Add Header
- self.table.setHorizontalHeaderLabels(horHeaders)
- layout.addWidget(self.label)
- layout.addWidget(self.table)
- self.button = QtGui.QPushButton("i am a button")
- layout.addWidget(self.button)
-
- def display_data(self, event):
- #pp.pprint(event)
- if type(event)==dict and event.has_key('row'):
- if event.has_key('string'):
- item=self.table.cellWidget(event['row'],event['col'])
- item.setText(event['string'])
- if event.has_key('PI'):
- #setPI
- PIcol=0
- self.table.item(event['row'],PIcol).setText(event['PI'])
- if event.has_key('AF'):
- #setAF
- PIcol=3
- self.table.item(event['row'],PIcol).setText(event['AF']['number'])
- if event.has_key('PSN'):
- #setPSN
- PSNcol=2
- item=self.table.cellWidget(event['row'],PSNcol)
- item.setText(event['PSN'])
- self.table.resizeColumnsToContents()
- #def reset_color(self):
- #for i in range(0,self.table.rowCount()):
- #for j in range(0,self.table.columnCount()):
- #item = self.table.item(i,j)
- ##code.interact(local=locals())
- ##print(item.type())
- #if item != '':
- #try:
- #item.setTextColor(QtCore.Qt.black)
- #except:
- #pass
- def onCLick(self):
- print("button clicked")
- #self.reset_color()
- #pp.pprint(event)
-if __name__ == "__main__":
- from PyQt4 import Qt
- import sys
-
- # def valueChanged(frequency):
- # print("Value updated - " + str(frequency))
-
- app = Qt.QApplication(sys.argv)
- # widget = RangeWidget(Range(0, 100, 10, 1, 100), valueChanged, "Test", "counter_slider", int)
- mainobj= rds_parser_table_qt_Signals()
- #mainobj=None
- widget = rds_parser_table_qt_Widget(mainobj,"TestLabel")
- widget.show()
- widget.setWindowTitle("Test Qt gui")
- widget.setGeometry(200,200,600,300)
- #code.interact(local=locals())
- sys.exit(app.exec_())
-
- widget = None
diff --git a/python/rds_parser_table_qt.py b/python/rds_parser_table_qt.py
index 6fa7bba..e965b6d 100644
--- a/python/rds_parser_table_qt.py
+++ b/python/rds_parser_table_qt.py
@@ -52,6 +52,8 @@ class rds_parser_table_qt(gr.sync_block):
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/"
reader = csv.reader(open(workdir+'RDS_ODA AIDs_names_only.csv'), delimiter=',', quotechar='"')
reader.next()#skip header
@@ -69,6 +71,12 @@ class rds_parser_table_qt(gr.sync_block):
reader = csv.reader(open(workdir+'event-list_code+de-name_sort.csv'), delimiter=',', quotechar='"')
#no header
self.ecl_dict=dict((int(rows[0]),rows[1]) for rows in reader)
+ #read PTY list
+ f=open(workdir+'pty-list.csv')
+ reader = csv.reader(f, delimiter=',', quotechar='"')
+ reader.next()#skip header
+ self.pty_dict=dict((int(rows[0]),rows[1]) for rows in reader)
+ f.close()
def handle_msg(self, msg, port):
#code.interact(local=locals())
array=pmt.to_python(msg)[1]
@@ -79,20 +87,40 @@ class rds_parser_table_qt(gr.sync_block):
else:
groupType=str(groupNR >> 4)+"B"
PI="%02X%02X" %(array[0],array[1])
+ TP=(array[2]>>2)&0x1
+ block2=(array[2]<<8)|(array[3]) #block2
+ PTY=(block2>>5)&0x1F
+ wrong_blocks=int(array[12])
+
#initialize dict 1st packet from station:
if not self.RDS_data.has_key(PI):
self.RDS_data[PI]={}
self.RDS_data[PI]["blockcounts"]={}
+ self.RDS_data[PI]["blockcounts"]["any"]=0
self.RDS_data[PI]["AID_list"]={}
self.RDS_data[PI]["PSN"]="_"*8
self.RDS_data[PI]["PSN_valid"]=[False]*8
self.RDS_data[PI]["AF"]={}
+ self.RDS_data[PI]["DI"]=[2,2,2,2]
print("found station %s"%PI)
-
+ self.RDS_data[PI]["blockcounts"]["any"]+=1
+ if self.RDS_data[PI]["blockcounts"]["any"]==5:
+ self.RDS_data[PI]["blockcounts"]["any"]=0
+ dots="."*self.RDS_data[PI]["blockcounts"]["any"]
+ self.signals.DataUpdateEvent.emit({'row':port,'PI':PI,'PTY':self.pty_dict[PTY],'TP':TP,'wrong_blocks':wrong_blocks,'dots':dots})
if (groupType == "0A"):#AF PSN
adr=array[3]&0b00000011
segment=self.decode_chars(chr(array[6])+chr(array[7]))
-
+ d=(array[3]>>2)&0x1
+ self.RDS_data[PI]["DI"][3-adr]=d
+ #DI[0]=d0 0=Mono 1=Stereo
+ #d1 Not artificial head Artificial head
+ #d2 Not compressed Compressed
+ #d3 Static PTY Dynamic PTY
+ TA=(array[3]>>4)&0x1
+ MS=(array[3]>>3)&0x1
+ flag_string="TP:%i, TA:%i, MS:%i, DI:%s"%(TP,TA,MS,str(self.RDS_data[PI]["DI"]))
+ self.signals.DataUpdateEvent.emit({'row':port,'PI':PI,'flags':flag_string})
#1110 0000 = no AF
#1110 0001 = 1AF
#1111 1001 = 25AF
@@ -129,13 +157,14 @@ class rds_parser_table_qt(gr.sync_block):
else:
textcolor="gray"
formatted_text=self.color_text(self.RDS_data[PI]["PSN"],adr*2,adr*2+2,textcolor,segmentcolor)
- self.signals.DataUpdateEvent.emit({'col':5,'row':port,'PI':PI,'PSN':formatted_text})
+ self.signals.DataUpdateEvent.emit({'row':port,'PI':PI,'PSN':formatted_text})
elif (groupType == "2A"):#RT radiotext
if(not self.RDS_data[PI].has_key("RT")):#initialize variables
self.RDS_data[PI]["RT"]="_"*64
self.RDS_data[PI]["RT_valid"]=[False]*64
+ self.RDS_data[PI]["RT_all_valid"]=False
else:
adr=array[3]&0b00001111
segment=self.decode_chars(chr(array[4])+chr(array[5])+chr(array[6])+chr(array[7]))
@@ -178,8 +207,8 @@ class rds_parser_table_qt(gr.sync_block):
formatted_text=self.color_text(self.RDS_data[PI]["RT"],adr*4,adr*4+4,textcolor,segmentcolor)
#print(self.RDS_data[PI]["RT"]+" valid:"+str(valid)+"valarr:"+str(self.RDS_data[PI]["RT_valid"]))
-
- self.signals.DataUpdateEvent.emit({'col':5,'row':port,'PI':PI,'string':formatted_text})
+ rtcol=self.colorder.index('text')
+ self.signals.DataUpdateEvent.emit({'col':rtcol,'row':port,'PI':PI,'string':formatted_text})
#code.interact(local=locals())
elif (groupType == "3A"):#ODA announcements (contain application ID "AID")
AID=(array[6]<<8)|(array[7])#combine 2 bytes into 1 block
@@ -205,15 +234,20 @@ class rds_parser_table_qt(gr.sync_block):
local_time_offset=0.5*((array[7])&0x1F)
if(offsetdir==1):
local_time_offset*=-1
- year=int((datecode - 15078.2) / 365.25)
+ year=int((datecode - 15078.2) / 365.25)
month=int((datecode - 14956.1 - int(year * 365.25)) / 30.6001)
day=datecode - 14956 - int(year * 365.25) - int(month * 30.6001)
if(month == 14 or month == 15):#no idea why -> annex g of RDS spec
year += 1;
month -= 13
year+=1900
- datestring="%02i.%02i.%4i, %02i:%02i (%+.1fh)" % (day,month,year,hours,minutes,local_time_offset)
- self.signals.DataUpdateEvent.emit({'col':4,'row':port,'PI':PI,'string':datestring})
+ #month was off by one different rounding in c and python?
+ month-=1
+ #datestring="%02i.%02i.%4i, %02i:%02i (%+.1fh)" % (day,month,year,hours,minutes,local_time_offset)
+ timestring="%02i:%02i (%+.1fh)" % (hours,minutes,local_time_offset)
+ datestring="%02i.%02i.%4i" % (day,month,year)
+ ctcol=self.colorder.index('time')
+ self.signals.DataUpdateEvent.emit({'col':ctcol,'row':port,'PI':PI,'string':timestring,'tooltip':datestring})
#TMC-alert-c (grouptype mostly 8A):
elif self.RDS_data[PI]["AID_list"].has_key(52550) and self.RDS_data[PI]["AID_list"][52550]["groupType"]==groupType:
tmc_x=array[3]&0x1f #lower 5 bit of block2
@@ -285,7 +319,7 @@ 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"]):
+ 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
rt=self.RDS_data[PI]["RT"]
if not tag1_type=="DUMMY_CLASS":
self.RDS_data[PI]["RT+"][tag1_type]=rt[tag1_start:tag1_start+tag1_len+1]
@@ -297,7 +331,9 @@ class rds_parser_table_qt(gr.sync_block):
artist=rt[tag1_start:tag1_start+tag1_len+1]
song=rt[tag2_start:tag2_start+tag2_len+1]
formatted_text="%s by %s"%(song,artist)
- self.signals.DataUpdateEvent.emit({'col':6,'row':port,'PI':PI,'string':formatted_text})
+ 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))
else:#other group
@@ -373,7 +409,7 @@ class rds_parser_table_qt_Widget(QtGui.QWidget):
self.setLayout(layout)
self.table=QtGui.QTableWidget(self)
self.table.setRowCount(5)
- self.table.setColumnCount(8)
+ self.table.setColumnCount(9)
self.table.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers) #disallow editing
#Data
empty_text32='________________________________'
@@ -382,14 +418,18 @@ class rds_parser_table_qt_Widget(QtGui.QWidget):
self.data = {'ID':range(1,6),
'freq':['','','',''],
'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':['','','',''],
'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)],
+ #'RT+':[ QtGui.QLabel() for i in range(4)],
+ 'quality':[ QtGui.QLabel() for i in range(4)],
'buttons':[]}
#Enter data onto Table
+ self.colorder=['ID','freq','name','PTY','AF','time','text','quality','buttons']
horHeaders = []
- for n, key in enumerate(['ID','freq','name','AF','time','text','RT+','buttons']):
+ for n, key in enumerate(self.colorder):
#for n, key in enumerate(sorted(self.data.keys())):
horHeaders.append(key)
for m, item in enumerate(self.data[key]):
@@ -407,31 +447,52 @@ class rds_parser_table_qt_Widget(QtGui.QWidget):
button.clicked.connect(self.onCLick)
#Add Header
self.table.setHorizontalHeaderLabels(horHeaders)
+ self.tmc_message_label=QtGui.QLabel("TMC messages:")
+ self.event_filter=QtGui.QLineEdit()#QPlainTextEdit ?
+ self.location_filter=QtGui.QLineEdit()
layout.addWidget(self.label)
layout.addWidget(self.table)
self.button = QtGui.QPushButton("i am a button")
layout.addWidget(self.button)
+ layout.addWidget(self.tmc_message_label)
+ layout.addWidget(self.event_filter)
+ layout.addWidget(self.location_filter)
def display_data(self, event):
#pp.pprint(event)
- if type(event)==dict and event.has_key('row'):
+ if type(event)==dict and event.has_key('row'):
+ 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'])
+ item.setText(quality_string)
+ if event.has_key('PTY'):
+ item=self.table.cellWidget(event['row'],self.colorder.index('PTY'))
+ item.setText(event['PTY'])
+ if event.has_key('flags'):
+ item=self.table.cellWidget(event['row'],self.colorder.index('PTY'))
+ item.setToolTip(Qt.QString(event['flags']))
if event.has_key('string'):
item=self.table.cellWidget(event['row'],event['col'])
item.setText(event['string'])
+ if event.has_key('tooltip'):
+ item=self.table.cellWidget(event['row'],event['col'])
+ item.setToolTip(Qt.QString(event['tooltip']))
if event.has_key('PI'):
#setPI
- PIcol=0
- rtpcol=6
+ 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']:
- self.table.cellWidget(event['row'],rtpcol).setText("")#clear RT+ on changed PI
+ #self.table.cellWidget(event['row'],rtpcol).setText("")#clear RT+ on changed PI
+ self.table.cellWidget(event['row'],rtcol).setToolTip(Qt.QString(""))
self.table.item(event['row'],PIcol).setText(event['PI'])
if event.has_key('AF'):
#setAF
- PIcol=3
+ PIcol=self.colorder.index('AF')
self.table.item(event['row'],PIcol).setText(event['AF']['number'])
if event.has_key('PSN'):
#setPSN
- PSNcol=2
+ PSNcol=self.colorder.index('name')
item=self.table.cellWidget(event['row'],PSNcol)
item.setText(event['PSN'])
self.table.resizeColumnsToContents()
diff --git a/swig/crfa_swig.i b/swig/crfa_swig.i
index b31d241..273d991 100644
--- a/swig/crfa_swig.i
+++ b/swig/crfa_swig.i
@@ -8,6 +8,8 @@
%include "crfa_swig_doc.i"
%{
+#include "crfa/rds_decoder.h"
%}
-
+%include "crfa/rds_decoder.h"
+GR_SWIG_BLOCK_MAGIC2(crfa, rds_decoder);