From 4596dbe3f1b491ec48d538c882b51f9168e8b3a2 Mon Sep 17 00:00:00 2001
From: csrichter
Date: Mon, 17 Apr 2017 19:31:54 +0200
Subject: [PATCH] added pilot quality block
---
grc/CMakeLists.txt | 3 +-
grc/multirds_pilot_quality.xml | 72 +
python/CMakeLists.txt | 3 +-
python/__init__.py | 1 +
python/pilot_quality.py | 54 +
python/rds_parser_table_qt.py | 4 +
python/rds_parser_table_qt.py.bak | 1346 -----------
..._table_qt.sync-conflict-20170315-102705.py | 1986 -----------------
8 files changed, 135 insertions(+), 3334 deletions(-)
create mode 100644 grc/multirds_pilot_quality.xml
create mode 100644 python/pilot_quality.py
delete mode 100644 python/rds_parser_table_qt.py.bak
delete mode 100644 python/rds_parser_table_qt.sync-conflict-20170315-102705.py
diff --git a/grc/CMakeLists.txt b/grc/CMakeLists.txt
index 37344d2..6b7985e 100644
--- a/grc/CMakeLists.txt
+++ b/grc/CMakeLists.txt
@@ -32,5 +32,6 @@ install(FILES
multirds_rds_decoder_redsea.xml
multirds_qtgui_range.xml
multirds_variable_setter.xml
- multirds_tmc_parser.xml DESTINATION share/gnuradio/grc/blocks
+ multirds_tmc_parser.xml
+ multirds_pilot_quality.xml DESTINATION share/gnuradio/grc/blocks
)
diff --git a/grc/multirds_pilot_quality.xml b/grc/multirds_pilot_quality.xml
new file mode 100644
index 0000000..03e5746
--- /dev/null
+++ b/grc/multirds_pilot_quality.xml
@@ -0,0 +1,72 @@
+
+
+ pilot_quality
+ multirds_pilot_quality
+ [multirds]
+ import multirds
+ multirds.pilot_quality($debug, $samp_rate, $fft_len, $carrier_freq,$gap_width, $msg_adr)
+
+
+ Debug
+ debug
+ False
+ bool
+
+ Enable
+ True
+
+
+ Disable
+ False
+
+
+
+ carrier_freq
+ carrier_freq
+ 19e3
+ float
+
+
+ gap_width
+ gap_width
+ 4e3
+ float
+
+
+ samp_rate
+ samp_rate
+ 240000
+ int
+
+
+ fft_len
+ fft_len
+ 2048
+ int
+
+
+ msg_adr
+ msg_adr
+ 3
+ int
+
+
+
+ in
+ float
+ $fft_len
+
+
+ out
+ message
+ 1
+
+
diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt
index dd33d2e..6a35b9c 100644
--- a/python/CMakeLists.txt
+++ b/python/CMakeLists.txt
@@ -44,7 +44,8 @@ GR_PYTHON_INSTALL(
qtgui_range.py
variable_setter.py
tmc_classes.py
- tmc_parser.py DESTINATION ${GR_PYTHON_DIR}/multirds
+ tmc_parser.py
+ pilot_quality.py DESTINATION ${GR_PYTHON_DIR}/multirds
)
########################################################################
diff --git a/python/__init__.py b/python/__init__.py
index e4aabb9..b26b12c 100644
--- a/python/__init__.py
+++ b/python/__init__.py
@@ -44,4 +44,5 @@ from decoder_compare import decoder_compare
from qtgui_range import qtgui_range
from variable_setter import variable_setter
from tmc_parser import tmc_parser
+from pilot_quality import pilot_quality
#
diff --git a/python/pilot_quality.py b/python/pilot_quality.py
new file mode 100644
index 0000000..5cdd87b
--- /dev/null
+++ b/python/pilot_quality.py
@@ -0,0 +1,54 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# Copyright 2017 <+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
+import pmt,code
+
+
+class pilot_quality(gr.sync_block):
+ """
+ docstring for block pilot_quality
+ """
+ def __init__(self, debug,samp_rate,fft_len,carrier_freq,gap_width,msg_adr):
+ gr.sync_block.__init__(self,
+ name="pilot_quality",
+ in_sig=[(np.float32,fft_len)],
+ out_sig=None)
+ #self.carrier_width=1
+ self.debug=debug
+ self.message_port_register_out(pmt.intern('out'))
+ self.carrier_index=int(carrier_freq*fft_len/float(samp_rate))
+ self.lowbound_index=int((carrier_freq-gap_width)*fft_len/float(samp_rate))
+ self.highbound_index=int((carrier_freq+gap_width)*fft_len/float(samp_rate))
+
+ def work(self, input_items, output_items):
+ in0 = input_items[0]
+ # <+signal processing here+>
+ for i,in_vec in enumerate(in0):
+ surrounding=np.mean(in_vec[self.lowbound_index:self.highbound_index])
+ carrier=np.mean(in_vec[self.carrier_index-1:self.carrier_index+1])
+ #code.interact(local=locals())
+ strength=abs(carrier-surrounding)
+ if self.debug:
+ print("strength: %f,carrier: %f, around:%f"%(strength,carrier,surrounding))
+ return len(input_items[0])
+
diff --git a/python/rds_parser_table_qt.py b/python/rds_parser_table_qt.py
index ef42aa5..7e90c32 100644
--- a/python/rds_parser_table_qt.py
+++ b/python/rds_parser_table_qt.py
@@ -260,6 +260,10 @@ class rds_parser_table_qt(gr.sync_block):#START
dots="."*self.RDS_data[PI]["blockcounts"]["any"]
self.RDS_data[PI]["wrong_block_ratio"]=wrong_block_ratio
self.signals.DataUpdateEvent.emit({'PI':PI,'wrong_block_ratio':wrong_block_ratio,'dots':dots})
+ elif pmt.to_long(pmt.car(msg))==3L: #carrier quality message
+ data=pmt.to_python(pmt.cdr(msg))
+ if self.debug:
+ print(data)
else: #elif pmt.to_long(pmt.car(msg))==0L
array=pmt.to_python(msg)[1]
diff --git a/python/rds_parser_table_qt.py.bak b/python/rds_parser_table_qt.py.bak
deleted file mode 100644
index 50ce382..0000000
--- a/python/rds_parser_table_qt.py.bak
+++ /dev/null
@@ -1,1346 +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.
-#
-from __future__ import print_function#print without newline print('.', end="")
-import numpy
-from gnuradio import gr
-import pmt,functools,csv,md5,collections,copy,sqlite3,atexit,time,re,sys
-#old imports: folium
-from datetime import datetime
-from datetime import timedelta
-import multirds.chart as chart
-from multirds.tmc_classes import tmc_dict,tmc_message
-
-from PyQt4 import Qt, QtCore, QtGui
-import pprint,code,pickle#for easier testing
-pp = pprint.PrettyPrinter()
-import cProfile, pstats, StringIO #for profiling
-pr = cProfile.Profile()
-
-#from threading import Timer#to periodically save DB
-
-from PyQt4.QtCore import QObject, pyqtSignal
-from bitstring import BitArray
-
-language="de"#currently supported: de, en (both partially)
-
-
-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):#START
- """
- docstring for block qtguitest
- """
- def goodbye(self):
- self.clean_data_and_commit_db()
- print("quitting rds parser table, closing db")
- if self.writeDB:
- #self.db.commit()
- self.db.close()
- def __init__(self,signals,nPorts,slot,freq,log,debug,workdir,writeDB,showTMC):
- gr.sync_block.__init__(self,
- name="RDS Table",
- in_sig=None,
- out_sig=None)
- if nPorts==1:
- self.message_port_register_in(pmt.intern('in'))
- self.set_msg_handler(pmt.intern('in'), functools.partial(self.handle_msg, port=0))
- else:
- for i in range(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.nPorts=nPorts
- self.message_port_register_in(pmt.intern('freq'))
- self.set_msg_handler(pmt.intern('freq'), self.set_freq)
- self.message_port_register_out(pmt.intern('ctrl'))
- self.message_port_register_out(pmt.intern('tmc_raw'))
-
- self.log=log
- self.debug=debug
- self.writeDB=writeDB
- self.showTMC=showTMC
- self.signals=signals
- self.RDS_data={}
- self.change_freq_tune=slot
- self.tuning_frequency=int(freq)
- self.printcounter=0
- self.ODA_application_names={}
- self.TMC_data={}
- self.IH_data={}
- self.decoder_frequencies={}
- self.decoders=[]
- for i in range(nPorts):
- self.decoders.append({'synced':False,'freq':None})
- #self.decoder_synced={}
- #self.colorder=['ID','freq','name','PTY','AF','time','text','quality','buttons']
- self.colorder=['ID','freq','name','buttons','PTY','AF','time','text','quality','RT+']
- self.workdir=workdir
- self.PI_dict={}#contains PI:numpackets (string:integer)
- self.tmc_messages=tmc_dict()
-
- if self.writeDB:
- #create new DB file
- db_name=workdir+'RDS_data'+datetime.now().strftime("%Y%m%d_%H%M%S")+'.db'
- db=sqlite3.connect(db_name, check_same_thread=False)
- self.db=db
- #create tables
- try:
- db.execute('''CREATE TABLE stations
- (PI text PRIMARY KEY UNIQUE,PSN text, freq real, PTY text,TP integer)''')
- db.execute('''CREATE TABLE groups
- (time text,PI text,PSN text, grouptype text,content blob)''')
- db.execute('''CREATE TABLE data
- (time text,PI text,PSN text, dataType text,data blob)''')
- db.execute('''CREATE TABLE grouptypeCounts
- (PI text,grouptype text,count integer,unique (PI, grouptype))''')
- db.execute('''CREATE TABLE TMC
- (hash text PRIMARY KEY UNIQUE,time text,PI text, F integer,event integer,location integer,DP integer,div integer,dir integer,extent integer,text text,multi text,rawmgm text)''')
- db.commit()
-
- except sqlite3.OperationalError:
- print("ERROR: tables already exist")
-
- #self.dbc.execute('''CREATE TABLE rtp
- # (time text,PI text,rtp_string text)''')
- reader = csv.reader(open(self.workdir+'RDS_ODA-AIDs_names_only.csv'), delimiter=',', quotechar='"')
- reader.next()#skip header
- for row in reader:
- self.ODA_application_names[int(row[0])]=row[1]
- #read location code list:
- reader = csv.reader(open(self.workdir+'LCL15.1.D-160122_utf8.csv'), delimiter=';', quotechar='"')
- reader.next()#skip header
- self.lcl_dict=dict((int(rows[0]),rows[1:]) for rows in reader)
- #read RT+ class name list:
- reader = csv.reader(open(self.workdir+'RTplus_classnames.csv'), delimiter=',', quotechar='"')
- reader.next()#skip header
- self.rtp_classnames=dict((int(rows[0]),rows[1]) for rows in reader)
- #read TMC-event list
- reader = csv.reader(open(self.workdir+'event-list_with_forecast_sort.csv'), delimiter=',', quotechar='"')
- reader.next()#skip header
- self.ecl_dict=dict((int(rows[0]),rows[1:]) for rows in reader)
- #Code,Text CEN-English,Text (German),Text (German) kein Quantifier,Text (Quantifier = 1),Text (Quantifier >1),N,Q,T,D,U,C,R ,Comment
- #N:nature (blank): information, F:forecast, S:silent
- #Q:quantifier type: (0..12) or blank (no quantifier)
- #T:duration type: D:dynamic, L:long lasting, in brackets or if time-of-day quantifier (no 7) is used in message -> no display, only for management
- #D:direction: 1:unidirectional, 2:bidirectional
- #U:urgency: blank: normal, X:extremely urgent, U:urgent
- #C: update class:
-
- #read update classes
- reader = csv.reader(open(self.workdir+'tmc_update_class_names.csv'), delimiter=',', quotechar='"')
- reader.next()#skip header, "code(C),english,german"
- if language=="de":
- self.tmc_update_class_names=dict((int(rows[0]),rows[2]) for rows in reader)#german names
- else:
- self.tmc_update_class_names=dict((int(rows[0]),rows[1]) for rows in reader)#english names
- #read supplementary information code list
- reader = csv.reader(open(self.workdir+'label6-supplementary-information-codes.csv'), delimiter=',', quotechar='"')
- reader.next()#skip header, "code,english,german"
- if language=="de":
- self.label6_suppl_info=dict((int(rows[0]),rows[2]) for rows in reader)#german
- else:
- self.label6_suppl_info=dict((int(rows[0]),rows[1]) for rows in reader)#english
- #read PTY list
- f=open(self.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()
- self.minute_count=0
- self.minute_count_max=0
- self.minute_count_timer=time.time()
- self.save_data_timer=time.time()
-
- atexit.register(self.goodbye)
-
- def clean_data_and_commit_db(self):
- for PI in self.PI_dict:
- self.PI_dict[PI]-=1
- #print(self.PI_dict)
- if self.writeDB:
- self.db.commit()
- f=open(self.workdir+'google_maps_markers.js', 'w')
- markerstring=self.tmc_messages.getMarkerString()
- markerstring+='\n console.log("loaded "+markers.length+" markers")'
- markerstring+='\n document.getElementById("errorid").innerHTML = "loaded "+markers.length+" markers";'
- f.write(markerstring)
- f.close()
- def update_freq(self):
- # " " is a tab character
- message_string="decoder frequencies:"
- for num in self.decoder_frequencies:
- freq=self.decoder_frequencies[num]
- if self.decoders[num]['synced']:
- message_string+=" %i:%0.1fM "% (num,freq/1e6)
- #print("'color:green'>%i:%0.1fM"% (num,freq/1e6))
- else:#elif self.decoders[num]['synced']==False:
- #print("'color:red'>%i:%0.1fM"% (num,freq/1e6))
- message_string+=" %i:%0.1fM "% (num,freq/1e6)
- message_string+=" tuned frequency:%0.1fM"%(self.tuning_frequency/1e6)
- self.signals.DataUpdateEvent.emit({'decoder_frequencies':message_string})
- #print(message_string)
- #self.signals.DataUpdateEvent.emit({'row':decoder_num,'freq':freq_str})
- #print("nr:%i freq:%s"%(tgtnum,freq_str))
- def set_freq_tune(self,freq):
- self.tuning_frequency=int(freq)
- self.update_freq()
- def set_freq(self,msg):
- m = pmt.symbol_to_string(msg)
- decoder_num=int(m.split()[0])-1#msgs are 1-indexed, decoder_num is 0-indexed
- freq_str=m.split()[1]
- try:
- freq=float(freq_str)
- self.decoder_frequencies[decoder_num]=freq
- freq_str="%i:%0.1fM"% (decoder_num,freq/1e6)
- except ValueError:
- pass#leave string as is
- self.update_freq()
- def init_data_for_PI(self,PI):
- self.RDS_data[PI]={}
- #self.RDS_data[PI]["blockcounts"]={}# no defaults (works aswell)
- #defaults are to keep colors in piechart consistent between stations:
-
- coverage_area={"0":"local,","1":"international,","2":"national,","3":"supranational,"}
- pi_str=""
- if list(PI)[1] in "456789ABCDEF":
- pi_str+="regional, "
- else:
- pi_str+=coverage_area[list(PI)[1]]
- bundeslaender={"0":"Baden-Württemberg","1":"Bayern","2":"Berlin","3":"Brandenburg","4":"Bremen und Bremerhaven",
- "5":"Hamburg","6":"Hessen","7":"Mecklenburg-Vorpommern","8":"Niedersachsen","9":"Nordrhein-Westfalen","A":"Rheinland-Pfalz",
- "B":"Saarland","C":"Sachsen","D":"Sachsen-Anhalt","E":"Schleswig-Holstein","F":"Thüringen"}
- if list(PI)[0] in "D1":
- pi_str+="germany?, "
- pi_str+=bundeslaender[list(PI)[2]]+", "
- pi_str+="NR:"+list(PI)[3]
- self.RDS_data[PI]["CC"]=list(PI)[0]
- self.RDS_data[PI]["PI-meaning"]=pi_str
- self.RDS_data[PI]["blockcounts"]={"0A":0,"1A":0,"2A":0,"3A":0,"4A":0,"6A":0,"8A":0,"12A":0,"14A":0}
- 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"]={"set":set(),"rxset":set()}
- self.RDS_data[PI]["TP"]=-1
- self.RDS_data[PI]["TA"]=-1
- self.RDS_data[PI]["PTY"]=""
- self.RDS_data[PI]["DI"]=[2,2,2,2]
- self.RDS_data[PI]["internals"]={"last_rt_tooltip":"","unfinished_TMC":{},"last_valid_rt":"","last_valid_psn":"","RT_history":[]}
- self.RDS_data[PI]["time"]={"timestring":"88:88","datestring":"00-00-0000","datetime":None}
- def handle_msg(self, msg, port):#port from 0 to 3
- if pmt.to_long(pmt.car(msg))==1L:
- data=pmt.to_python(pmt.cdr(msg))
- #print("port:%i, data: %s"%(port,data))
- self.decoders[port]['synced']=data
- self.update_freq()
- else: #elif pmt.to_long(pmt.car(msg))==0L
- array=pmt.to_python(msg)[1]
-
- if time.time()-self.save_data_timer > 10:#every 10 seconds
- self.save_data_timer=time.time()
- self.clean_data_and_commit_db()
-
- if time.time()-self.minute_count_timer > 3:#every 3 second
- self.minute_count_max=self.minute_count
- self.signals.DataUpdateEvent.emit({'group_count':self.minute_count,'group_count_max':self.minute_count_max})
- self.minute_count=0
- self.minute_count_timer=time.time()
- #pr.enable()#disabled-internal-profiling
- self.minute_count+=1
- #self.signals.DataUpdateEvent.emit({'group_count':self.minute_count,'group_count_max':self.minute_count_max})
- if self.writeDB:
- #db=sqlite3.connect(self.db_name)
- db=self.db
-
-
- groupNR=array[2]&0b11110000
- groupVar=array[2]&0b00001000
- if (groupVar == 0):
- groupType=str(groupNR >> 4)+"A"
- else:
- groupType=str(groupNR >> 4)+"B"
- #if self.debug:
- #PI=str(port)+"_%02X%02X" %(array[0],array[1])
- #else:
- #PI="%02X%02X" %(array[0],array[1])
- 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])
-
- try:
- self.PI_dict[PI]+=1
- except KeyError:
- pass
-
- if not self.RDS_data.has_key(PI):#station invalid/new
- if not self.PI_dict.has_key(PI):#1st group
- self.PI_dict[PI]=1
- return#dont decode further if not yet valid
- elif self.PI_dict[PI]>2:#count station as valid if more than 2 packets received
- self.init_data_for_PI(PI)#initialize dict for station
- if self.log:
- print("found station %s"%PI)
- else:
- return#dont decode further if not yet valid
-
- if self.decoder_frequencies.has_key(port):
- freq=self.decoder_frequencies[port]
- freq_str="%i:%0.1fM"% (port,freq/1e6)
- self.RDS_data[PI]["tuned_freq"]=freq
- #self.signals.DataUpdateEvent.emit({'PI':PI,'freq':freq_str})
- if self.RDS_data[PI]["blockcounts"].has_key(groupType):
- self.RDS_data[PI]["blockcounts"][groupType] +=1 #increment
- else:
- self.RDS_data[PI]["blockcounts"][groupType] = 1 #initialize (1st group of this type)
- self.RDS_data[PI]["blockcounts"]["any"]+=1
- if self.RDS_data[PI]["blockcounts"]["any"]==5:
- self.RDS_data[PI]["blockcounts"]["any"]=0
- if self.writeDB:
- t=(str(PI),groupType,self.RDS_data[PI]["blockcounts"][groupType])#TODO only update DB every few seconds
- db.execute("INSERT OR REPLACE INTO grouptypeCounts (PI,grouptype,count) VALUES (?,?,?)",t)
- dots="."*self.RDS_data[PI]["blockcounts"]["any"]
- self.RDS_data[PI]["TP"]=TP
- self.RDS_data[PI]["PTY"]=self.pty_dict[PTY]
-
- self.signals.DataUpdateEvent.emit({'row':port,'PI':PI,'PTY':self.pty_dict[PTY],'TP':TP,'wrong_blocks':wrong_blocks,'dots':dots})
-
-
-
- #add any received groups to DB (slow)
- #content="%02X%02X%02X%02X%02X" %(array[3]&0x1f,array[4],array[5],array[6],array[7])
- #t=(str(datetime.now()),PI,self.RDS_data[PI]["PSN"],groupType,content)
- #db.execute("INSERT INTO groups VALUES (?,?,?,?,?)",t)
-
- 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#decoder information
- #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
- self.RDS_data[PI]["TA"]=TA
- #style='font-family:Courier New;color:%s'
- flag_string="TP:%i, TA:%i, MS:%i, DI:%s "%(TP,TA,MS,str(self.RDS_data[PI]["DI"]))
- pty_colored=self.RDS_data[PI]["PTY"]
- if TP==1:
- if TA==1:
- color="red"
- elif TA==0:
- color="green"
- else:
- color="yellow"
- pty_colored="%s "%(color,self.RDS_data[PI]["PTY"])
-
- self.signals.DataUpdateEvent.emit({'row':port,'PI':PI,'flags':flag_string,'PTY':pty_colored})
-
- #224 1110 0000 = no AF
- #225 1110 0001 = 1AF
- #249 1111 1001 = 25AF
- fillercode=205#1100 1101
- if not self.RDS_data[PI]["AF"].has_key("main") and self.RDS_data[PI].has_key("tuned_freq"):
- #if self.RDS_data[PI].has_key("tuned_freq"):#update main freq even if one exists -> DB problem
- freq=self.decode_AF_freq(array[4])
- if freq==self.RDS_data[PI]["tuned_freq"]:
- self.RDS_data[PI]["AF"]["main"]=freq
- if self.log:
- print("main frequency found in 0A: station:%s, freq:%0.1fM"% (self.RDS_data[PI]["PSN"],freq/1e6))
- freq_str="0A:%0.1fM"% (freq/1e6)
- self.signals.DataUpdateEvent.emit({'PI':PI,'freq':freq_str})
- t=(PI,self.RDS_data[PI]["PSN"],float(freq),self.RDS_data[PI]["PTY"],int(self.RDS_data[PI]["TP"]))
- if self.writeDB:
- db.execute("INSERT INTO stations (PI,PSN,freq,PTY,TP) VALUES (?,?,?,?,?)",t)
- freq=self.decode_AF_freq(array[5])
- if freq==self.RDS_data[PI]["tuned_freq"]:
- self.RDS_data[PI]["AF"]["main"]=freq
- if self.log:
- print("main frequency found in 0A: station:%s, freq:%0.1fM"% (self.RDS_data[PI]["PSN"],freq/1e6))
- freq_str="0A:%0.1fM"% (freq/1e6)
- self.signals.DataUpdateEvent.emit({'PI':PI,'freq':freq_str})
- t=(PI,self.RDS_data[PI]["PSN"],float(freq),self.RDS_data[PI]["PTY"],int(self.RDS_data[PI]["TP"]))
- if self.writeDB:
- db.execute("INSERT INTO stations (PI,PSN,freq,PTY,TP) VALUES (?,?,?,?,?)",t)
- if self.RDS_data[PI].has_key("tuned_freq") :#TODO add secondary freqs
- freq=self.decode_AF_freq(array[4])
- diff=abs(freq-self.RDS_data[PI]["tuned_freq"])
- if diff<100000:
- self.RDS_data[PI]["AF"]["rxset"].add(freq)
- freq=self.decode_AF_freq(array[5])
- diff=abs(freq-self.RDS_data[PI]["tuned_freq"])
- if diff<100000:
- self.RDS_data[PI]["AF"]["rxset"].add(freq)
-
- if(array[4]>= 224 and array[4]<= 249):
- #print("AF1 detected")
- self.RDS_data[PI]["AF"]['number']=array[4]-224
- #self.RDS_data[PI]["AF"]['main']=self.decode_AF_freq(array[5])
- self.signals.DataUpdateEvent.emit({'row':port,'PI':PI,'AF':self.RDS_data[PI]["AF"]})
- if(array[5]>= 224 and array[5]<= 249):
- print("AF2 detected (shouldn't happen) %s"%array[5])
-
-
- #add frequencies to set
- self.RDS_data[PI]["AF"]["set"].add(self.decode_AF_freq(array[4]))
- self.RDS_data[PI]["AF"]["set"].add(self.decode_AF_freq(array[5]))
- try:
- self.RDS_data[PI]["AF"]["set"].remove(0)#remove control characters
- except KeyError:
- pass
-
- name_list=list(self.RDS_data[PI]["PSN"])
- 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.RDS_data[PI]["PSN"]="_"*8
- self.RDS_data[PI]["PSN_valid"]=[False]*8
- self.RDS_data[PI]["PSN_valid"][adr*2:adr*2+2]=[True] *2
- self.RDS_data[PI]["PSN"]="".join(name_list)
- #determine if text is valid
- valid=True
- for i in range(0,8):
- if (not self.RDS_data[PI]["PSN_valid"][i]):
- valid = False
- if(valid):
- #textcolor="black"
- textcolor=""#use default color (white if background is black)
- if not self.RDS_data[PI]["internals"]["last_valid_psn"]==self.RDS_data[PI]["PSN"]:#ignore duplicates
- t=(str(datetime.now()),PI,self.RDS_data[PI]["PSN"],"PSN_valid",self.RDS_data[PI]["PSN"])
- if self.writeDB:
- db.execute("INSERT INTO data (time,PI,PSN,dataType,data) VALUES (?,?,?,?,?)",t)
- t=(self.RDS_data[PI]["PSN"],PI)
- if self.writeDB:
- db.execute("UPDATE OR IGNORE stations SET PSN=? WHERE PI IS ?",t)
- self.RDS_data[PI]["internals"]["last_valid_psn"]=self.RDS_data[PI]["PSN"]
- else:
- textcolor="gray"
- formatted_text=self.color_text(self.RDS_data[PI]["PSN"],adr*2,adr*2+2,textcolor,segmentcolor)
- self.signals.DataUpdateEvent.emit({'row':port,'PI':PI,'PSN':formatted_text})
- elif (groupType == "1A"):#PIN programme item number
- #wer nutzt 1A gruppen?
- #antenne1: variants: 0(ECC),
- PIN=(array[6]<<8)|(array[7])
- SLC=(array[4]<<8)|(array[5])&0xfff#slow labeling code
- radio_paging=array[3]&0x1f
- LA=array[4]>>7#linkage actuator
- variant=(array[4]>>4)&0x7
- PIN_day=(PIN>>11)&0x1f
- PIN_hour=(PIN>>6)&0x1f
- PIN_minute=PIN&0x3f
- PIN_valid= PIN_day in range(1,32) and PIN_hour in range(0,24) and PIN_minute in range(0,60)
- if PIN_valid:
- self.RDS_data[PI]["PIN"]=[PIN_day,PIN_hour,PIN_minute]
- data_string="variant:%i,SLC:%04X,PIN (valid):%s "%(variant,SLC,str([PIN_day,PIN_hour,PIN_minute]))
- else:
- data_string="variant:%i,SLC:%04X,PIN:%04X "%(variant,SLC,PIN)
- #%02X%02X%02X%02X%02X
-
- t=(str(datetime.now()),PI,self.RDS_data[PI]["PSN"],"PIN",data_string)
- if self.writeDB:
- db.execute("INSERT INTO data (time,PI,PSN,dataType,data) VALUES (?,?,?,?,?)",t)
- if self.debug and not variant==0:#print if not seen before
- print("PI:%s PSN:%s uses variant %i of 1A"%(PI,self.RDS_data[PI]["PSN"],variant))
- if variant==0:
- paging=array[4]&0xf
- extended_country_code=array[5]
- self.RDS_data[PI]["ECC"]=extended_country_code
- if self.debug:
- print("1A variant 0: PI:%s PSN:%s,ECC:%s"%(PI,self.RDS_data[PI]["PSN"],hex(extended_country_code)))
- elif variant==1:
- TMC_info=SLC
- elif variant==2:
- paging_info=SLC
- elif variant==3:
- language_codes=SLC
- if self.debug:
- print("PI:%s PSN:%s,language_codes:%s"%(PI,self.RDS_data[PI]["PSN"],hex(language_codes)))
- elif variant==6:
- #for use by broadcasters
- if self.debug:
- print("PI:%s PSN:%s uses variant 6 of 1A"%(PI,self.RDS_data[PI]["PSN"]))
- elif variant==7:
- ESW_channel_identification=SLC
- #end of 1A decode
- elif (groupType == "2A"):#RT radiotext
- if(not self.RDS_data[PI].has_key("RT_0")):#initialize variables
- self.RDS_data[PI]["RT_0"]={"RT":"_"*64,"RT_valid":[False]*64,"RT_all_valid":False}
- self.RDS_data[PI]["RT_1"]={"RT":"_"*64,"RT_valid":[False]*64,"RT_all_valid":False}
- #self.RDS_data[PI]["RT"]="_"*64
- #self.RDS_data[PI]["RT_valid"]=[False]*64
- #self.RDS_data[PI]["RT_all_valid"]=False
- self.RDS_data[PI]["RT_last_ab_flag"]=2
-
- adr= array[3]&0b00001111
- ab_flag=(array[3]&0b00010000)>>4
- #print("PI:%s, AB:%i"%(PI,ab_flag))
-
-
- #if self.RDS_data[PI]["RT_last_ab_flag"] !=ab_flag:#AB flag changed -> clear text
- # self.RDS_data[PI]["RT"]="_"*64
- # self.RDS_data[PI]["RT_valid"]=[False]*64
- # self.RDS_data[PI]["RT_last_ab_flag"] =ab_flag
- self.RDS_data[PI]["RT_last_ab_flag"] =ab_flag
-
- #segment=self.decode_chars(chr(array[4])+chr(array[5])+chr(array[6])+chr(array[7]))
- segment=chr(array[4])+chr(array[5])+chr(array[6])+chr(array[7])#EDIT:latedecode
-
- #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.RDS_data[PI]["RT_"+str(ab_flag)]["RT"])
- #determine text length:
- try:
- text_end=text_list.index('\r')
- except ValueError:
- text_end=64 #assume whole string is important
- pass
- predicted=False
- 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.RDS_data[PI]["RT_"+str(ab_flag)]["RT"]="_"*64
- self.RDS_data[PI]["RT_"+str(ab_flag)]["RT_valid"]=[False]*64
- #predict RT from last texts:
- for rt in self.RDS_data[PI]["internals"]["RT_history"]:
- if rt[adr*4:adr*4+4]==list(segment):
- self.RDS_data[PI]["RT_"+str(ab_flag)]["RT"]="".join(rt)
- predicted=True
-
- self.RDS_data[PI]["RT_"+str(ab_flag)]["RT_valid"][adr*4:adr*4+4]=[True] *4
- if not predicted:
- self.RDS_data[PI]["RT_"+str(ab_flag)]["RT"]="".join(text_list)
-
- #determine if (new) text is valid
- self.RDS_data[PI]["RT_"+str(ab_flag)]["RT_all_valid"]=True
- for i in range(0,text_end):
- if (not self.RDS_data[PI]["RT_"+str(ab_flag)]["RT_valid"][i]):
- self.RDS_data[PI]["RT_"+str(ab_flag)]["RT_all_valid"] = False
- if(self.RDS_data[PI]["RT_"+str(ab_flag)]["RT_all_valid"]):
- #textcolor="black"
- textcolor=""#use default color (white if background is black)
- l=list(self.RDS_data[PI]["RT_"+str(ab_flag)]["RT"])
- rt="".join(l[0:text_end])#remove underscores(default symbol) after line end marker
- if not self.RDS_data[PI]["internals"]["last_valid_rt"]==rt:#ignore duplicates #TODO add 2nd order duplicates ABAB
- self.RDS_data[PI]["internals"]["RT_history"].append(l)
- if len(self.RDS_data[PI]["internals"]["RT_history"])>10:#only store last 10 RTs
- self.RDS_data[PI]["internals"]["RT_history"].pop(0)
- if self.writeDB:
- t=(str(datetime.now()),PI,self.RDS_data[PI]["PSN"],"RT",self.decode_chars(rt))
- db.execute("INSERT INTO data (time,PI,PSN,dataType,data) VALUES (?,?,?,?,?)",t)
- self.RDS_data[PI]["internals"]["last_valid_rt"]=rt
- try:#save rt+ if it exist
- if self.writeDB:
- t=(str(datetime.now()),PI,self.RDS_data[PI]["PSN"],"RT+",self.decode_chars(str(self.RDS_data[PI]["RT+"])))
- db.execute("INSERT INTO data (time,PI,PSN,dataType,data) VALUES (?,?,?,?,?)",t)
- except KeyError:
- pass#no rt+ -> dont save
- else:
- textcolor="gray"
- display_text=self.decode_chars(self.RDS_data[PI]["RT_"+str(ab_flag)]["RT"].split("\r")[0])
- formatted_text=self.color_text(display_text,adr*4,adr*4+4,textcolor,segmentcolor)
- rtcol=self.colorder.index('text')
- self.signals.DataUpdateEvent.emit({'col':rtcol,'row':port,'PI':PI,'string':formatted_text})
-
- elif (groupType == "3A"):#ODA announcements (contain application ID "AID")
- AID=(array[6]<<8)|(array[7])#combine 2 bytes into 1 block
- app_data=(array[4]<<8)|(array[5])#content defined by ODA-app
- app_group_raw=array[3]&0x1f #group type in which this app is sent
- if (app_group_raw&0x1 == 0):
- app_group=str(app_group_raw >> 1)+"A"
- else:
- app_group=str(app_group_raw >> 1)+"B"
-
- if not self.RDS_data[PI]["AID_list"].has_key(AID):#new ODA found
- try:
- app_name=self.ODA_application_names[AID]
- except KeyError:
- if self.debug:
- print("ERROR: ODA-app-id (AID) '%i' not found in list on station %s, app group:%s"%(AID,app_group,PI))
- app_name="unknown"
- self.RDS_data[PI]["AID_list"][AID]={}
- self.RDS_data[PI]["AID_list"][AID]["groupType"]=app_group
- self.RDS_data[PI]["AID_list"][AID]["app_name"]=app_name
- self.RDS_data[PI]["AID_list"][AID]["app_data"]=app_data
- if AID==52550:#TMC alert-c initialize
- self.RDS_data[PI]["AID_list"][AID]["provider name"]="________"
- if self.log:
- print("new ODA: AID:%i, name:'%s', app_group:%s, station:%s" %(AID,app_name,app_group,PI))
- #decode 3A group of TMC
- if AID==52550:#TMC alert-c (continuously update)
- variant=app_data>>14
- if variant==0:
- self.RDS_data[PI]["AID_list"][AID]["LTN"]=(app_data>>6)&0x3f#location table number (6 bits)
- self.RDS_data[PI]["AID_list"][AID]["AFI"]=(app_data>>5)&0x1#alternative frequency indicator
- self.RDS_data[PI]["AID_list"][AID]["M"]=(app_data>>4)&0x1#transmission mode indicator
- #Message Geographical Scope:
- self.RDS_data[PI]["AID_list"][AID]["scope"]=""
- if (app_data>>3)&0x1==1:
- self.RDS_data[PI]["AID_list"][AID]["scope"]+="I"#international (EUROROAD)
- if (app_data>>2)&0x1==1:
- self.RDS_data[PI]["AID_list"][AID]["scope"]+="N"#national
- if (app_data>>1)&0x1==1:
- self.RDS_data[PI]["AID_list"][AID]["scope"]+="R"#regional
- if (app_data>>0)&0x1==1:
- self.RDS_data[PI]["AID_list"][AID]["scope"]+="U"#urban
- #self.RDS_data[PI]["AID_list"][AID]["I"]=(app_data>>3)&0x1#international (EUROROAD)
- #self.RDS_data[PI]["AID_list"][AID]["N"]=(app_data>>2)&0x1#national
- #self.RDS_data[PI]["AID_list"][AID]["R"]=(app_data>>1)&0x1#regional
- #self.RDS_data[PI]["AID_list"][AID]["U"]=(app_data>>0)&0x1#urban
- elif variant==1:
- self.RDS_data[PI]["AID_list"][AID]["SID"]=(app_data>>6)&0x3f#service identifier
- #timing parameters (used to switch away from TMC station without missing messages):
- self.RDS_data[PI]["AID_list"][AID]["G"]=(app_data>>12)&0x3#gap parameter
- self.RDS_data[PI]["AID_list"][AID]["activity_time"]=(app_data>>4)&0x3
- self.RDS_data[PI]["AID_list"][AID]["window_time"]=(app_data>>2)&0x3
- self.RDS_data[PI]["AID_list"][AID]["delay_time"]=(app_data>>0)&0x3
- elif self.debug:
- print("unknown variant %i in TMC 3A group"%variant)
- send_pmt = pmt.pmt_to_python.pmt_from_dict({
- "type":"3A_meta",
- "data":self.RDS_data[PI]["AID_list"][AID]})
- self.tableobj.message_port_pub(pmt.intern('tmc_raw'), send_pmt)
- elif (groupType == "4A"):#CT clock time
- bits=BitArray('uint:8=%i,uint:8=%i,uint:8=%i,uint:8=%i,uint:8=%i'%tuple(array[3:8]))
- spare,datecode,hours,minutes,offsetdir,local_time_offset = bits.unpack("uint:6,uint:17,uint:5,uint:6,uint:1,uint:5")
- local_time_offset*=0.5
- #datecode=((array[3] & 0x03) << 15) | (array[4] <<7)|((array[5] >> 1) & 0x7f)#modified julian date
- if datecode==0:
- #do not update!!
- if self.debug:
- print("station:%s sent empty 4A group"%self.RDS_data[PI]["PSN"])
- else:
- #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
-
- date=datetime(1858,11,17)+timedelta(days=int(datecode))#convert from MJD (modified julian date)
-
- timestring="%02i:%02i (%+.1fh)" % (hours,minutes,local_time_offset)
- datestring=date.strftime("%d.%m.%Y")
- ctcol=self.colorder.index('time')
- self.signals.DataUpdateEvent.emit({'col':ctcol,'row':port,'PI':PI,'string':timestring,'tooltip':datestring})
- t=(str(datetime.now()),PI,self.RDS_data[PI]["PSN"],"CT",datestring+" "+timestring+"; datecode(MJD):"+str(datecode))
- self.RDS_data[PI]["time"]["timestring"]=timestring
- self.RDS_data[PI]["time"]["datestring"]=datestring
- try:
- self.RDS_data[PI]["time"]["datetime"]=datetime(date.year,date.month,date.day,hours,minutes)+timedelta(hours=local_time_offset)
- except ValueError:
- print("ERROR: could not interpret time or date:"+datestring+" "+timestring)
- if self.writeDB:
- db.execute("INSERT INTO data (time,PI,PSN,dataType,data) VALUES (?,?,?,?,?)",t)
- elif (groupType == "6A"):#IH inhouse data -> save for analysis
- """In House Data:
-{'130A': {'1E1077FFFF': {'count': 1,
- 'last_time': '2016-12-08 16:26:54.767596'},
- '1F23022015': {'count': 1,
- 'last_time': '2016-12-08 16:26:56.341271'}},
- 'D00F': {'1E1023FFFF': {'count': 1,
- 'last_time': '2016-12-08 16:26:54.769165'},
- '1F28032008': {'count': 3,
- 'last_time': '2016-12-08 16:26:58.272420'}}}"""
- ih_data="%02X%02X%02X%02X%02X" %(array[3]&0x1f,array[4],array[5],array[6],array[7])
- if not self.IH_data.has_key(PI):
- self.IH_data[PI]={}
- if not self.IH_data[PI].has_key(ih_data):
- self.IH_data[PI][ih_data]={}
- self.IH_data[PI][ih_data]["count"]=0
- self.IH_data[PI][ih_data]["count"]+=1
- self.IH_data[PI][ih_data]["last_time"]=str(datetime.now())
- #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 alert-C
- tmc_x=array[3]&0x1f #lower 5 bit of block2
- tmc_y=(array[4]<<8)|(array[5]) #block3
- tmc_z=(array[6]<<8)|(array[7])#block4
- send_pmt = pmt.pmt_to_python.pmt_from_dict({
- "type":"alert-c",
- "PI":PI,
- "PSN":self.RDS_data[PI][PSN],
- "TMC_X":tmc_x,
- "TMC_Y":tmc_y,
- "TMC_Z":tmc_z
- })
- self.tableobj.message_port_pub(pmt.intern('tmc_raw'), send_pmt)
- tmc_hash=md5.new(str([PI,tmc_x,tmc_y,tmc_z])).hexdigest()
- tmc_T=tmc_x>>4 #0:TMC-message 1:tuning info/service provider name
- tmc_F=int((tmc_x>>3)&0x1) #identifies the message as a Single Group (F = 1) or Multi Group (F = 0)
- Y15=int(tmc_y>>15)
- #timestring=self.RDS_data[PI]["time"]["timestring"]
- datetime_received=self.RDS_data[PI]["time"]["datetime"]
- if tmc_T == 0:
- if tmc_F==1:#single group
- tmc_msg=tmc_message(PI,tmc_x,tmc_y,tmc_z,datetime_received,self)
- self.print_tmc_msg(tmc_msg)
- elif tmc_F==0 and Y15==1:#1st group of multigroup
- ci=int(tmc_x&0x7)
- tmc_msg=tmc_message(PI,tmc_x,tmc_y,tmc_z,datetime_received,self)
- #if self.RDS_data[PI]["internals"]["unfinished_TMC"].has_key(ci):
- #print("overwriting parital message")
- self.RDS_data[PI]["internals"]["unfinished_TMC"][ci]={"msg":tmc_msg,"time":time.time()}
- else:
- ci=int(tmc_x&0x7)
- if self.RDS_data[PI]["internals"]["unfinished_TMC"].has_key(ci):
- tmc_msg=self.RDS_data[PI]["internals"]["unfinished_TMC"][ci]["msg"]
- tmc_msg.add_group(tmc_y,tmc_z)
- age=time.time()-self.RDS_data[PI]["internals"]["unfinished_TMC"][ci]["time"]
- t=(time.time(),PI,age,ci,tmc_msg.is_complete)
- #print("%f: continuing message PI:%s,age:%f,ci:%i complete:%i"%t)
- self.RDS_data[PI]["internals"]["unfinished_TMC"][ci]["time"]=time.time()
- if tmc_msg.is_complete:
- self.print_tmc_msg(tmc_msg)#print and store message
- del self.RDS_data[PI]["internals"]["unfinished_TMC"][tmc_msg.ci]#delete finished message
- else:
- #if not ci==0:
- #print("ci %i not found, discarding"%ci)
- pass
-
- else:#alert plus or provider info
- adr=tmc_x&0xf
- if 4 <= adr and adr <= 9:
- #seen variants 4569, 6 most often
- #print("TMC-info variant:%i"%adr)
- if adr==4 or adr==5:#service provider name
- segment=self.decode_chars(chr(array[4])+chr(array[5])+chr(array[6])+chr(array[7]))
- if self.debug:
- print("TMC-info adr:%i (provider name), segment:%s, station:%s"%(adr,segment,self.RDS_data[PI]["PSN"]))
- if self.RDS_data[PI]["AID_list"].has_key(52550):
- text_list=list(self.RDS_data[PI]["AID_list"][52550]["provider name"])
- seg_adr_start=(adr-4)*4#start of segment
- text_list[seg_adr_start:seg_adr_start+4]=segment
- self.RDS_data[PI]["AID_list"][52550]["provider name"]="".join(text_list)
- if adr== 7:#freq of tuned an mapped station (not seen yet)
- freq_TN=tmc_y>>8
- freq_ON=tmc_y&0xff#mapped frequency
- if self.debug:
- print("TMC-info: TN:%i, station:%s"%(freq_TN,self.RDS_data[PI]["PSN"]))
- self.RDS_data[PI]["TMC_TN"]=freq_TN
- else:
- if self.debug:
- print("alert plus on station %s (%s)"%(PI,self.RDS_data[PI]["PSN"]))#(not seen yet)
-
- #self.tableobj.RDS_data["D301"]["AID_list"][52550]["provider name"]="test____"
- #RadioText+ (grouptype mostly 12A):
- elif self.RDS_data[PI]["AID_list"].has_key(19415) and self.RDS_data[PI]["AID_list"][19415]["groupType"]==groupType:#RT+
- if not self.RDS_data[PI].has_key("RT+"):
- #self.RDS_data[PI]["RT+"]={"history":{},"last_item_toggle_bit":2}
- self.RDS_data[PI]["RT+"]={"last_item_toggle_bit":2}
- self.RDS_data[PI]["RT+_history"]={}
- self.RDS_data[PI]["internals"]["RT+_times"]={}
- #self.RDS_data[PI]["RT+"]["last_item_toggle_bit"]=2
- A3_data=self.RDS_data[PI]["AID_list"][19415]["app_data"]
- template_number=A3_data&0xff
- SCB=(A3_data >> 8)&0x0f#server control bit
- CB_flag=(A3_data>>12)&0x1 #is set if template available
- rtp_message= ((array[3]&0x1f)<<32)|(array[4]<<24)|(array[5]<<16)|(array[6]<<8)|(array[7])
- item_toggle_bit=(rtp_message>>36)&0x1
- item_running_bit=(rtp_message>>35)&0x1
- tag1=(rtp_message>>17)&(2**18-1)#6+6+6
- tag2=(rtp_message)&(2**17-1)#6+6+5
- tag1_type=self.rtp_classnames[int(tag1>>12)]
- tag2_type=self.rtp_classnames[int(tag2>>11)]
- tag1_start=int((tag1>>6)&(2**6-1))
- tag1_len=int(tag1&(2**6-1))
- tag2_start=int((tag2>>5)&(2**6-1))
- tag2_len=int(tag2&(2**5-1))
- if not self.RDS_data[PI]["RT+"]["last_item_toggle_bit"] == item_toggle_bit: #new item
- #self.RDS_data[PI]["RT+"]["history"][str(datetime.now())]=self.RDS_data[PI]["internals"]["last_rt_tooltip"]
- t=(str(datetime.now()),PI,self.RDS_data[PI]["PSN"],"RT+",str(self.RDS_data[PI]["RT+"]))
- if self.writeDB:
- db.execute("INSERT INTO data (time,PI,PSN,dataType,data) VALUES (?,?,?,?,?)",t)
- self.RDS_data[PI]["RT+_history"][str(datetime.now())]=copy.deepcopy(self.RDS_data[PI]["RT+"])#save old item
- self.RDS_data[PI]["RT+"]["last_item_toggle_bit"] = item_toggle_bit
- rtcol=self.colorder.index('text')
- if self.debug:
- print("toggle bit changed on PI:%s, cleared RT-tt"%PI)
- self.signals.DataUpdateEvent.emit({'col':rtcol,'row':port,'PI':PI,'tooltip':""})
- if self.RDS_data[PI].has_key("RT_0"):
- ab_flag=self.RDS_data[PI]["RT_last_ab_flag"]
- rt=self.RDS_data[PI]["RT_"+str(ab_flag)]["RT"]
- rt_valid=self.RDS_data[PI]["RT_"+str(ab_flag)]["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]
- self.RDS_data[PI]["internals"]["RT+_times"][tag1_type]=time.time()
- 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]
- self.RDS_data[PI]["internals"]["RT+_times"][tag2_type]=time.time()
- #check outdated tags:
- for tagtype in self.RDS_data[PI]["internals"]["RT+_times"].keys():#.keys() makes copy to avoid RuntimeError: dictionary changed size during iteration
- age=time.time()-self.RDS_data[PI]["internals"]["RT+_times"][tagtype]
- if age>90:#delete if older than 90 sek#TODO delete if toggle bit changes?, delete if tag changes? (title change -> delete artist)
- del self.RDS_data[PI]["internals"]["RT+_times"][tagtype]
- del self.RDS_data[PI]["RT+"][tagtype]
-
-
- tags="ir:%i,it:%i"%(item_running_bit,item_toggle_bit)
- rtpcol=self.colorder.index('RT+')
- self.signals.DataUpdateEvent.emit({'col':rtpcol,'row':port,'PI':PI,'string':tags})
- if(tag2_type=="ITEM.TITLE" and self.RDS_data[PI].has_key("RT_0")):#TODO remove duplicate code
- ab_flag=self.RDS_data[PI]["RT_last_ab_flag"]
- rt=self.RDS_data[PI]["RT_"+str(ab_flag)]["RT"]
- rt_valid=self.RDS_data[PI]["RT_"+str(ab_flag)]["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')
- #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))
- elif (groupType == "14A"):#EON enhanced other networks
- #TN = tuned network, ON=other network
- if not self.RDS_data[PI].has_key("EON"):
- self.RDS_data[PI]["EON"]={}
- TP_ON=(array[3]>>4)&0x1
- PI_ON="%02X%02X" %(array[6],array[7])
- variant=array[3]&0xf
- self.signals.DataUpdateEvent.emit({'PI':PI_ON,'TP':TP_ON})
- if not self.RDS_data.has_key(PI_ON):
- self.init_data_for_PI(PI_ON)
- self.RDS_data[PI_ON]["TP"]=TP_ON
- self.PI_dict[PI_ON]=0#initialize dict, even if no packets received
- if self.log:
- print("found station %s via EON on station %s"%(PI_ON,PI))
- if not self.RDS_data[PI]["EON"].has_key(PI_ON):
- self.RDS_data[PI]["EON"][PI_ON]={}
- self.RDS_data[PI]["EON"][PI_ON]["PSN"]="_"*8
- if variant in range(4):#variant 0..3 -> PS_ON
- segment=self.decode_chars(chr(array[4])+chr(array[5]))
- name_list=list(self.RDS_data[PI_ON]["PSN"])
- #name_list=list(self.RDS_data[PI]["EON"][PI_ON]["PSN"])
- name_list[variant*2:variant*2+2]=segment
- if (name_list[variant*2:variant*2+2]==list(segment)):#segment already there
- segmentcolor="purple"
- elif(name_list[variant*2:variant*2+2]==['_']*2): #segment new
- segmentcolor="purple"
- name_list[variant*2:variant*2+2]=segment
- else:#name changed (böse)
- segmentcolor="red"
- name_list=['_']*8 #reset name
- name_list[variant*2:variant*2+2]=segment
- #reset stored text:
- self.RDS_data[PI_ON]["PSN"]="_"*8
- self.RDS_data[PI_ON]["PSN_valid"]=[False]*8
- self.RDS_data[PI_ON]["PSN_valid"][variant*2:variant*2+2]=[True] *2
- PS_ON_str="".join(name_list)
- self.RDS_data[PI_ON]["PSN"]=PS_ON_str
- self.RDS_data[PI]["EON"][PI_ON]["PSN"]=PS_ON_str
- #determine if text is valid
- valid=True
- for i in range(0,8):
- if (not self.RDS_data[PI_ON]["PSN_valid"][i]):
- valid = False
- if(valid):
- #textcolor="black"
- textcolor=""#use default color (white if background is black)
-
- else:
- textcolor="gray"
- formatted_text=self.color_text(self.RDS_data[PI_ON]["PSN"],variant*2,variant*2+2,textcolor,segmentcolor)
- self.RDS_data[PI]["EON"][PI_ON]["PSN"]=PS_ON_str
- self.RDS_data[PI_ON]["PSN"]=PS_ON_str
- #formatted_text="%s "%("purple",PS_ON_str)
- self.signals.DataUpdateEvent.emit({'PI':PI_ON,'PSN':formatted_text})
- try:
- t=(PI_ON,self.RDS_data[PI_ON]["PSN"],float(self.RDS_data[PI_ON]["AF"]["main"]),self.RDS_data[PI_ON]["PTY"],int(self.RDS_data[PI_ON]["TP"]))
- if self.writeDB:
- db.execute("INSERT OR REPLACE INTO stations (PI,PSN,freq,PTY,TP) VALUES (?,?,?,?,?)",t)
- except KeyError:
- #not all info present -> no db update
- pass
- if variant==4:#AF_ON
- if self.debug:
- print("AF_ON method A")#TODO
- if variant in range(5,10):#variant 5..9 -> mapped freqs
- freq_TN=self.decode_AF_freq(array[4])
- freq_ON=self.decode_AF_freq(array[5])
- #lock in tuned network if freq_TN matches decoder frequency
- if(self.RDS_data[PI].has_key("tuned_freq") and freq_TN==self.RDS_data[PI]["tuned_freq"]and not self.RDS_data[PI]["AF"].has_key("main")):
- if self.log:
- print("main frequency found in 14A: station:%s, freq:%0.1fM"% (self.RDS_data[PI]["PSN"],freq_TN/1e6))
- self.RDS_data[PI]["AF"]["main"]=freq_TN
- freq_str="EON_TN:%0.1fM"% (freq_TN/1e6)
- self.signals.DataUpdateEvent.emit({'PI':PI,'freq':freq_str})
- #lock in ON if TN is locked in
- if(self.RDS_data[PI]["AF"].has_key("main") and self.RDS_data[PI]["AF"]["main"]==freq_TN and not self.RDS_data[PI_ON]["AF"].has_key("main")):
- if self.log:
- print("mapped frequency found in 14A: station:%s, freq:%0.1fM"% (self.RDS_data[PI_ON]["PSN"],freq_ON/1e6))
- self.RDS_data[PI_ON]["AF"]["main"]=freq_ON
- freq_str="EON:%0.1fM"% (freq_ON/1e6)
- self.signals.DataUpdateEvent.emit({'PI':PI_ON,'freq':freq_str})
- #print("mapped freq in variant %i:, %i->%i"%(variant,freq_TN,freq_ON))
- if variant==13:#PTY and TA of ON
- PTY_ON=array[4]>>3
- TA_ON=array[5]&0x1
- self.RDS_data[PI]["EON"][PI_ON]["TA_ON"]=TA_ON
- self.RDS_data[PI]["EON"][PI_ON]["PTY_ON"]=PTY_ON
- self.RDS_data[PI_ON]["TA"]=TA_ON
- self.RDS_data[PI_ON]["PTY"]=self.pty_dict[PTY_ON]
- self.signals.DataUpdateEvent.emit({'PI':PI_ON,'PTY':self.pty_dict[PTY_ON],'TA':TA_ON})
- #rest is reserved
- if variant==14:#programme item number of ON
- PIN_ON=(array[4]<<8)|(array[5])
- elif (groupType == "8A"):
- if self.debug:
- print("8A without 3A on PI:%s"%PI)
- #else:#other group
- #print("group of type %s not decoded on station %s"% (groupType,PI))
- if 1==1:
- #printdelay=50
- printdelay=500
- self.printcounter+=0#printing disabled
- if self.printcounter == printdelay and self.debug:
-
- for key in self.RDS_data:
- if self.RDS_data[key].has_key("PSN"):
- psn=self.RDS_data[key]["PSN"]
- else:
- psn="?"
- print("%s(%s):"%(psn,key),end="")
- pp.pprint(self.RDS_data[key]["blockcounts"])
- if self.RDS_data[key].has_key("RT+"):
- print("RT+:",end="")
- pp.pprint(self.RDS_data[key]["RT+"])
- self.printcounter=0
-
-
- pr.disable() #disabled-internal-profiling
- #end of handle_msg
- def print_tmc_msg(self,tmc_msg):
- try:
- PI=tmc_msg.PI
- tmc_F=tmc_msg.is_single
- tmc_hash=tmc_msg.tmc_hash
- refloc_name=""
- reflocs=tmc_msg.location.reflocs
- if not self.TMC_data.has_key(tmc_hash):#if message new
- try:
- self.TMC_data[tmc_hash]=tmc_msg
- if self.showTMC:
- self.signals.DataUpdateEvent.emit({'TMC_log':tmc_msg,'multi_str':tmc_msg.multi_str()})
- #t=(str(datetime.now()),PI,self.RDS_data[PI]["PSN"],"ALERT-C",message_string.decode("utf-8"))
- #self.db.execute("INSERT INTO data (time,PI,PSN,dataType,data) VALUES (?,?,?,?,?)",t)
- timestring=self.RDS_data[PI]["time"]["timestring"]
- if self.writeDB:
- message_string=tmc_msg.db_string()
- t=(tmc_hash,timestring,PI, tmc_F,tmc_msg.event.ecn,int(tmc_msg.location.lcn),tmc_msg.tmc_DP,tmc_msg.tmc_D,tmc_msg.tmc_dir,tmc_msg.tmc_extent,message_string.decode("utf-8"),tmc_msg.multi_str().decode("utf-8"),str(tmc_msg.debug_data))
- self.db.execute("INSERT INTO TMC (hash,time,PI, F,event,location,DP,div,dir,extent,text,multi,rawmgm) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)",t)
- except Exception as e:
- print(e)
- raise
- #print("line 1064")
-
- except KeyError:
- #print("location '%i' not found"%tmc_location)
- pass
- def print_results(self):
- s = StringIO.StringIO()
- sortby = 'cumulative'
- ps = pstats.Stats(pr, stream=s).sort_stats(sortby)
- ps.print_stats()
- print(s.getvalue())
- def decode_AF_freq(self,freq_raw):
- #if freq_raw in range(1,205):#1..204 BAD coding -> memory usage
- if 1<=freq_raw <=204:
- return(87500000+freq_raw*100000)#returns int
- #return(87.5e6+freq_raw*0.1e6)#returns float
- else:
- return(0)
- def ref_locs(self,loc,name_string):
- if(loc==34196):#europe
- return(name_string)
- else:
- try:
- locarray=self.lcl_dict[loc]
- aref=int(locarray[6])
- loc_name=locarray[4]
- return(self.ref_locs(aref,name_string+","+loc_name))
- #return(loc_name)
- except KeyError:
- return(name_string)
-
- def decode_chars(self,charstring):
- alphabet={
- 0b0010:u" !\#¤%&'()*+,-./",
- 0b0011:u"0123456789:;<=>?",
- 0b0100:u"@ABCDEFGHIJKLMNO",
- 0b0101:u"PQRSTUVWXYZ[\]―_",
- 0b0110:u"‖abcdefghijklmno",
- 0b0111:u"pqrstuvwxyz{|}¯ ",
- 0b1000:u"áàéèíìóòúùÑÇŞßiIJ",
- 0b1001:u"âäêëîïôöûüñçşǧıij",
- 0b1010:u"ªα©‰Ǧěňőπ€£$←↑→↓",
- 0b1011:u"º¹²³±İńűµ¿÷°¼½¾§",
- 0b1100:u"ÁÀÉÈÍÌÓÒÚÙŘČŠŽĐĿ",
- 0b1101:u"ÂÄÊËÎÏÔÖÛÜřčšžđŀ",
- 0b1110:u"ÃÅÆŒŷÝÕØÞŊŔĆŚŹŦð",
- 0b1111:u"ãåæœŵýõøþŋŕćśźŧ "}#0xff should not occur (not in standard) (but occured 2017-03-04-9:18 , probably transmission error)
-
- #charlist=list(charstring)
- return_string=""
- for i,char in enumerate(charstring):
- #split byte
- alnr=(ord(char)&0xF0 )>>4 #upper 4 bit
- index=ord(char)&0x0F #lower 4 bit
- if ord(char)<= 0b00011111:#control code
- if ord(char)==0x0D or ord(char)==0x00:#end of message SWR uses: \r\0\0\0 for last block (\0 fill 4 char segment)
- #return_string+="\r"
- return_string+=char
- else:
- return_string+="{%02X}"%ord(char)#output control code
-# elif ord(char)<= 0b01111111: #real encoding slightly different from ascii
-# return_string+=char#use ascii
- else:
- try:
- return_string+=alphabet[alnr][index]
- #return_string+=unichr(ord(char))#TODO: properly decide for UTF8 or EBU charset
- except KeyError:
- return_string+="?%02X?"%ord(char)#symbol not decoded
- print("symbol not decoded: "+"?%02X?"%ord(char)+"in string:"+return_string)
- pass
- return return_string
- def color_text(self, text, start,end,textcolor,segmentcolor):
- #formatted_text="%s %s %s "% (textcolor,text[:start],segmentcolor,text[start:end],textcolor,text[end:])
- #formatted_text="%s %s %s "% (textcolor,text[:start],segmentcolor,text[start:end],textcolor,text[end:])
- formatted_text=("%s "*3)% (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,tableobj,showTMC):
- #print("gui initializing")self.tableobj.RDS_data["D3A2"]
- self.signals = signals
- self.tableobj=tableobj
- self.showTMC=showTMC
- 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)#title of table disabled to save space
- #layout.addWidget(self.label)
- self.setLayout(layout)
- #self.decoder_to_PI={}
- self.PI_to_row={}
- self.table=QtGui.QTableWidget(self)
- rowcount=0
- self.table.setRowCount(rowcount)
- self.table.setColumnCount(10)
- self.table.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers) #disallow editing
-
- #self.colorder=['ID','freq','name','buttons','PTY','AF','time','text','quality']
- self.colorder=tableobj.colorder
- ##button.clicked.connect(self.getDetails)
-
- layout.addWidget(self.table)
- self.table.setHorizontalHeaderLabels(self.colorder)
- #self.table.setMaximumHeight(300)#TODO use dynamic value
-
- button_layout = Qt.QHBoxLayout()
- codebutton = QtGui.QPushButton("code.interact")
- codebutton.clicked.connect(self.onCLick)
- button_layout.addWidget(codebutton)
- ih_button = QtGui.QPushButton("show IH data")
- ih_button.clicked.connect(self.showIHdata)
- button_layout.addWidget(ih_button)
- save_button = QtGui.QPushButton("save")
- save_button.clicked.connect(self.saveData)
- button_layout.addWidget(save_button)
- print_button = QtGui.QPushButton("print profile")
- print_button.clicked.connect(self.printProfile)
- button_layout.addWidget(print_button)
- mode_button = QtGui.QPushButton("mode")
- mode_button.clicked.connect(self.switchMode)
- button_layout.addWidget(mode_button)
- layout.addLayout(button_layout)
- label_layout = Qt.QHBoxLayout()
- self.freq_label=QtGui.QLabel("decoder frequencies:")
- #self.freq_label.setTextFormat(QtCore.Qt.RichText)
- #self.freq_label.setTextFormat(QtCore.Qt.PlainText)
- self.count_label=QtGui.QLabel("count:")
- label_layout.addWidget(self.freq_label)
- label_layout.addWidget(self.count_label)
- layout.addLayout(label_layout)
- #TODO set different minsize if TMC is shown
- self.setMinimumSize(Qt.QSize(500,40*self.tableobj.nPorts))
- if self.showTMC:
- self.tmc_message_label=QtGui.QLabel("TMC messages:")
- self.event_filter=QtGui.QLineEdit()#QPlainTextEdit ?
- self.location_filter=QtGui.QLineEdit(u"Baden-Württemberg")
- #self.location_filter=QtGui.QLineEdit(u"")
- self.event_filter.returnPressed.connect(self.filterChanged)
- self.location_filter.returnPressed.connect(self.filterChanged)
-
- filter_layout = Qt.QHBoxLayout()
- filter_layout.addWidget(QtGui.QLabel("event filter:"))
- filter_layout.addWidget(self.event_filter)
- filter_layout.addWidget(QtGui.QLabel("location filter:"))
- filter_layout.addWidget(self.location_filter)
-
- layout.addLayout(filter_layout)
- layout.addWidget(self.tmc_message_label)
- self.logOutput = Qt.QTextEdit()
- self.logOutput.setReadOnly(True)
- self.logOutput.setLineWrapMode(Qt.QTextEdit.NoWrap)
- self.logOutput.setMaximumHeight(150)
- font = self.logOutput.font()
- font.setFamily("Courier")
- font.setPointSize(10)
- layout.addWidget(self.logOutput)
- self.lastResizeTime=0
- self.clip = QtGui.QApplication.clipboard()
- #self.cb.clear(mode=cb.Clipboard )
- #self.cb.setText("Clipboard Text", mode=cb.Clipboard)
- def filterChanged(self):
- print("filter changed")
- ef=unicode(self.event_filter.text().toUtf8(), encoding="UTF-8").lower()
- lf=unicode(self.location_filter.text().toUtf8(), encoding="UTF-8").lower()
- self.logOutput.clear()
- #filters=[{"type":"location", "str":u"Baden-Württemberg"}]
- filters=[{"type":"location", "str":lf},{"type":"event", "str":ef}]
- self.logOutput.append(Qt.QString.fromUtf8(self.tableobj.tmc_messages.getLogString(filters)))
- #self.logOutput.append(Qt.QString.fromUtf8(self.tableobj.tmc_messages.getLogString([])))
- def keyPressEvent(self, e):
- if (e.modifiers() & QtCore.Qt.ControlModifier) and len(self.table.selectedRanges())>0:
- selected = self.table.selectedRanges().pop()
- selected.leftColumn()
- selected.topRow()
- if e.key() == QtCore.Qt.Key_C: #copy
- try:
- qs = self.table.cellWidget(selected.topRow(),selected.leftColumn()).text()#get QString from table
- s=re.sub("<.*?>","", str(qs))#remove html tags
- self.clip.setText(s)
- except Exception as e:
- print(e)
- print("no text, cant copy")
- def insert_empty_row(self):
- rowPosition = self.table.rowCount()
- self.table.insertRow(rowPosition)
- #for col in range(self.table.columnCount()-1):#all labels except in last column -> buttons
-# self.table.setCellWidget(rowPosition,col,QtGui.QLabel())
- #initialize labels everywhere:
- for col in range(self.table.columnCount()):
- self.table.setCellWidget(rowPosition,col,QtGui.QLabel())
- button_layout = Qt.QHBoxLayout()
- details_button=QtGui.QPushButton("Detail")
- details_button.clicked.connect(functools.partial(self.getDetails, row=rowPosition))
- button_layout.addWidget(details_button)
- #2017-03-17 disabled LR buttons
- #left_button=QtGui.QPushButton("L")
- #left_button.clicked.connect(functools.partial(self.setAudio, row=rowPosition,audio_channel="left"))
- #button_layout.addWidget(left_button)
- #right_button=QtGui.QPushButton("R")
- #right_button.clicked.connect(functools.partial(self.setAudio, row=rowPosition,audio_channel="right"))
- #button_layout.addWidget(right_button)
-
- cellWidget = QtGui.QWidget()
- cellWidget.setLayout(button_layout)
- button_col=3
- self.table.setCellWidget(rowPosition,button_col,cellWidget)
- def display_data(self, event):
- #pp.pprint(event)
- if type(event)==dict and event.has_key('group_count'):
- self.count_label.setText("count:%02i, max:%i"%(event['group_count'],event['group_count_max']))
- if type(event)==dict and event.has_key('decoder_frequencies'):
- self.freq_label.setText(event['decoder_frequencies'])
- if type(event)==dict and event.has_key('TMC_log') and self.showTMC:
- tmc_msg=event['TMC_log']
- ef=unicode(self.event_filter.text().toUtf8(), encoding="UTF-8").lower()
- lf=unicode(self.location_filter.text().toUtf8(), encoding="UTF-8").lower()
- filters=[{"type":"location", "str":lf},{"type":"event", "str":ef}]
- if self.tableobj.tmc_messages.matchFilter(tmc_msg,filters):
- self.logOutput.append(Qt.QString.fromUtf8(tmc_msg.log_string()))
- self.logOutput.append(Qt.QString.fromUtf8(tmc_msg.multi_str()))
- if type(event)==dict and event.has_key('TMC_log_str')and self.showTMC:
- ef=unicode(self.event_filter.text().toUtf8(), encoding="UTF-8").lower()
- lf=unicode(self.location_filter.text().toUtf8(), encoding="UTF-8").lower()
- text=unicode(event['TMC_log_str'], encoding="UTF-8").lower()
- if not text.find(lf)==-1 and not text.find(ef)==-1:
- self.logOutput.append(Qt.QString.fromUtf8(event['TMC_log_str']))
- if type(event)==dict and event.has_key('PI'):
- PI=event['PI']
- if not self.PI_to_row.has_key(PI):
- self.PI_to_row[PI]=len(self.PI_to_row)#zero for first PI seen, then count up
- self.insert_empty_row()
- row=self.PI_to_row[PI]
- PIcol=self.colorder.index('ID')
- self.table.cellWidget(row,PIcol).setText(PI)
-
- if event.has_key('freq'):
- freqcol=self.colorder.index('freq')
- item=self.table.cellWidget(row,freqcol)
- item.setText(event['freq'])
- if event.has_key('wrong_blocks'):
- item=self.table.cellWidget(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(row,self.colorder.index('PTY'))
- tt=item.toolTip()
- item.setText(event['PTY'])
- item.setToolTip(tt)
- if event.has_key('flags'):
- item=self.table.cellWidget(row,self.colorder.index('PTY'))
- item.setToolTip(Qt.QString(event['flags']))
- if event.has_key('string'):
- item=self.table.cellWidget(row,event['col'])
- item.setText(event['string'])
- if event.has_key('tooltip'):
- item=self.table.cellWidget(row,event['col'])
- item.setToolTip(Qt.QString(event['tooltip']))
- if event.has_key('AF'):
- #setAF
- PIcol=self.colorder.index('AF')
- self.table.cellWidget(row,PIcol).setText(str(event['AF']['number']))
- if event.has_key('PSN'):
- #setPSN
- PSNcol=self.colorder.index('name')
- item=self.table.cellWidget(row,PSNcol)
- item.setText(event['PSN'])
- if time.time()-self.lastResizeTime > 2:#every 2 seconds
- self.table.resizeColumnsToContents()
- self.lastResizeTime=time.time()
- #end of display-data
- def printProfile(self):
- self.tableobj.print_results()
- def switchMode(self):
- #print("mode switch message sent")
- send_pmt = pmt.pmt_to_python.pmt_from_dict({"cmd":"switch mode"})
- #send_pmt = pmt.string_to_symbol("switch mode")
- self.tableobj.message_port_pub(pmt.intern('ctrl'), send_pmt)
- def saveData(self):
- filename="RDS_data_"+str(datetime.now())+".txt"
- f=open(self.tableobj.workdir+filename,"w")
- rds_data=copy.deepcopy(self.tableobj.RDS_data)
- for PI in sorted(rds_data):
- try:
- del rds_data[PI]['PSN_valid']
- del rds_data[PI]['RT_valid']
- except KeyError:
- pass
- f.write("Data:%s"%pp.pformat(rds_data))
- f.write("\n\nIn House Data:\n%s"%pp.pformat(self.tableobj.IH_data))
- f.close()
- print("data saved in file %s"%filename)
- def showIHdata(self):
- view=Qt.QDialog()
- l=QtGui.QLabel("In House Data:\n%s"%pp.pformat(self.tableobj.IH_data))
- l.setTextInteractionFlags(QtCore.Qt.TextSelectableByMouse |QtCore.Qt.TextSelectableByKeyboard)
- l.setWordWrap(True)
- #self.IH_data
- layout=Qt.QVBoxLayout()
- layout.addWidget(l)
- view.setLayout(layout)
- view.exec_()
- def setAudio(self,row,audio_channel):
-
- PIcol=self.colorder.index('ID')
- PI=str(self.table.cellWidget(row,PIcol).text())
- freq=int(self.tableobj.RDS_data[PI]['AF']['main'])
- #print("setaudio row:%i, chan:%s, PI:%s,freq:%i"%(row,audio_channel,PI,freq))
- send_pmt = pmt.pmt_to_python.pmt_from_dict({"cmd":"set_audio_freq","chan":audio_channel,"freq":freq})
- self.tableobj.message_port_pub(pmt.intern('ctrl'), send_pmt)
- #catch:
- #print("no freq, cant set decoder")#show notification? popup: too intrusive, log: maybe not visible, other possibility?
- #print("freq not in RX BW")#automatically shift freq-tune?
- def getDetails(self,row):
- PIcol=self.colorder.index('ID')
- PI=str(self.table.cellWidget(row,PIcol).text())
- view = chart.DialogViewer()
- if self.tableobj.PI_dict.has_key(PI) and self.tableobj.PI_dict[PI]>3:#dont print piechart if no packets received (detected via EON)
- table=chart.DataTable()
- table.addColumn('groupType')
- table.addColumn('numPackets')
- blockcounts=copy.deepcopy(self.tableobj.RDS_data[PI]['blockcounts'])
- 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.setGraph(mychart)
- #view.resize(360, 240)
- #view.resize(380, 550)
- rds_data=copy.deepcopy(self.tableobj.RDS_data[PI])
- try:
- del rds_data['blockcounts']
- del rds_data['PSN_valid']
- del rds_data["RT_0"]['RT_valid']
- del rds_data["RT_1"]['RT_valid']
- rds_data['internals']['RT_history']=["".join(rt) for rt in rds_data['internals']['RT_history']]#combine char lists into strings (more compact)
- except KeyError:
- pass
- l=QtGui.QLabel("Data:%s"%pp.pformat(rds_data))
- l.setTextInteractionFlags(QtCore.Qt.TextSelectableByMouse |QtCore.Qt.TextSelectableByKeyboard)
- l.setWordWrap(True)
- #l=QtGui.QLabel("Data:")
-
- #view.layout().addWidget(l)
-
- scrollArea = QtGui.QScrollArea(self)
- scrollArea.setWidgetResizable(True)
- scrollArea.setWidget(l)
- view.layout().addWidget(scrollArea)
- view.setWindowTitle(self.tableobj.RDS_data[PI]["PSN"])
- view.exec_()
- def onCLick(self):
- print("button clicked")
- code.interact(local=locals())
-if __name__ == "__main__":
- from PyQt4 import Qt
- import sys
-
-
- app = Qt.QApplication(sys.argv)
- 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)
- sys.exit(app.exec_())
-
- widget = None
-
-
diff --git a/python/rds_parser_table_qt.sync-conflict-20170315-102705.py b/python/rds_parser_table_qt.sync-conflict-20170315-102705.py
deleted file mode 100644
index 7eaf4b4..0000000
--- a/python/rds_parser_table_qt.sync-conflict-20170315-102705.py
+++ /dev/null
@@ -1,1986 +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.
-#
-from __future__ import print_function#print without newline print('.', end="")
-import numpy
-from gnuradio import gr
-import pmt,functools,csv,md5,collections,copy,sqlite3,atexit,time,re,sys
-#old imports: folium
-from datetime import datetime
-from datetime import timedelta
-import multirds.chart as chart
-
-from PyQt4 import Qt, QtCore, QtGui
-import pprint,code,pickle#for easier testing
-pp = pprint.PrettyPrinter()
-import cProfile, pstats, StringIO #for profiling
-pr = cProfile.Profile()#disabled-internal-profiling
-
-#from threading import Timer#to periodically save DB
-
-from PyQt4.QtCore import QObject, pyqtSignal
-from bitstring import BitArray
-
-language="de"#currently supported: de, en (both partially)
-
-SUFFIXES = {1: 'st', 2: 'nd', 3: 'rd'}
-def ordinal(num):
- # I'm checking for 10-20 because those are the digits that
- # don't follow the normal counting scheme.
- if 10 <= num % 100 <= 20:
- suffix = 'th'
- else:
- # the second parameter is a default.
- suffix = SUFFIXES.get(num % 10, 'th')
- return str(num) + suffix
-class tmc_event:
- def __init__(self,ecn,tableobj):
- self.tableobj=tableobj
- self.ecn=ecn
- self.text_raw="##Error##"
- self.name="##Error##"
- self.length_str=None
- self.speed_limit_str=None
- try:
- #Code,Text CEN-English,Text (German),Text (German) kein Quantifier,Text (Quantifier = 1),Text (Quantifier >1),N,Q,T,D,U,C,R ,Comment
- event_array=self.tableobj.ecl_dict[ecn]
- self.text_noQ=event_array[2]
- if language=="de":
- self.text_raw=event_array[1]
- self.name=self.text_noQ
- else:
- self.text_raw=event_array[0]#CEN-english
- self.name=re.sub("\(([^()]+)\)","",self.text_raw)#removes everything between parentheses (assume no quantifier is used)
- self.text_singleQ=event_array[3]
- self.text_pluralQ=event_array[4]
- self.nature=event_array[5]#N:nature (blank): information, F:forecast, S:silent
- if event_array[0]=="message cancelled":
- self.is_cancellation = True
- else:
- self.is_cancellation = False
- self.quantifierType=event_array[6]#Q:quantifier type: (0..12) or blank (no quantifier)
- self.durationType=event_array[7]#T:duration type: D:dynamic, L:long lasting, in brackets or if time-of-day quantifier (no 7) is used in message -> no display, only for management
- directionality=event_array[8]#D:directionality: 1:unidirectional, 2:bidirectional, cancellation messages dont have directionality
- self.is_unidirectional=True if directionality=="1" else False
- self.is_bidirectional=True if directionality=="2" else False
- self.urgency=event_array[9]#U:urgency: blank: normal, X:extremely urgent, U:urgent
- self.updateClass=int(event_array[10])#C: update class:
- self.updateClassName=self.tableobj.tmc_update_class_names[self.updateClass]
- self.phrase_code=event_array[11]#R: phrase code
- #04789
- #if not self.quantifierType=="" and not self.quantifierType=="0" and not self.quantifierType=="4":
- #print("type: %s, C:%s"%(self.quantifierType,self.updateClassName))
- self.is_valid=True
- except KeyError:
- print("event '%i' not found"%ecn)
- self.is_valid=False
- def change_directionality(self):
- self.is_unidirectional=not self.is_unidirectional
- self.is_bidirectional=not self.is_bidirectional
- def add_length(self,data):#from label2
- self.length_str="%i km"%mgm_tag.length_to_km(data.uint)
- #self.name=self.name.replace("(L)",self.length_str)
- def add_speed_limit(self,data):#from label3
- self.speed_limit_str="%i km/h"%(data.uint*5)
- def add_quantifier(self,data,bitLength):#from label 4
- self.name=self.text_raw#default
- Q_raw=data.uint
- if Q_raw==0:#binary zero represents highest value
- Q=32
- else:
- Q=Q_raw
- quantifier_string="type:%s,raw:%i"%(self.quantifierType,Q)
- #print(str(self.ecn)+", "+quantifier_string+", "+str(bitLength)+", "+str(data)+", "+self.text_raw)
- if self.quantifierType=="":
- print("cannot add quantifier %i to event ecn:%i"%(Q_raw,self.ecn))
- elif self.quantifierType=="0":#small numbers
- if(Q <= 28):
- quantifier_string=str(Q)
- else:
- quantifier_string=str(30+(Q-29)*2)#30,32,34,36
- #print(quantifier_string)
- self.name=self.text_pluralQ.replace("(Q)",quantifier_string)
- elif self.quantifierType=="1":#numbers
- numbers=[1,2,3,4,10,20,30,40,50,60,70,80,90,100,150,200,250,300,350,400,450,500,550,600,650,700,750,800,850,900,950,1000]
- quantifier_string=str(numbers[Q-1])
- elif self.quantifierType=="2":#z.b. für sichtweiten, e.g. for visibility
- quantifier_string="%i m"%(Q*10)#TODO meter oder metern?
- self.name=self.text_pluralQ.replace("(Q)",quantifier_string)
- #quantifier_string="%i Metern"%Q*10
- #self.name=self.text_pluralQ.replace("Q)",quantifier_string+")")
- elif self.quantifierType=="4":
- speed=Q*5#in kmh
- #quantifier_string="von bis zu %i km/h"%speed
- quantifier_string="%i km/h"%speed
- elif self.quantifierType=="7":
- hours=int((Q-1)/6)
- minutes=((Q-1)%6)*10
- quantifier_string="%i:%i"%(hours,minutes)
- #print(quantifier_string)
- elif self.quantifierType=="8":
- if Q<=100:
- weight=Q*0.1
- else:
- weight=10+0.5*(Q-100)
- quantifier_string="%it"%weight
- self.name=self.text_pluralQ.replace("(Q)",quantifier_string)
- elif self.quantifierType=="9":
- if Q<=100:
- length=Q*0.1
- else:
- length=10+0.5*(Q-100)
- quantifier_string="%.1fm"%length
- self.name=self.text_pluralQ.replace("(Q)",quantifier_string)
- else:#other quantifier
- self.name=self.text_raw+"; Q(%s)=%s"%(self.quantifierType,quantifier_string)
- def __str__(self):
- if self.is_valid:
- retstr=self.name
- if not self.length_str == None:
- retstr=self.name.replace("(L)",self.length_str)
- if not self.speed_limit_str == None:
- if language=="de":
- retstr+=" (geschw. begrenzt: %s)"%self.speed_limit_str
- else:
- retstr+=" (speed limit: %s)"%self.speed_limit_str
- return retstr
- else:
- return("invalid event, ecn:%i"%self.ecn)
- def __repr__(self):
- return "ecn:%i"%self.ecn
-class tmc_location:
- #def get_extent_location(self,extent,direction):
- #__recursion_get_extent_location(self,extent,direction)
- #def __recursion_get_extent_location(self,loc,extent,direction): #direction: 0:pos, 1:neg
- def get_extent_location(self,loc,extent,direction): #direction: 0:pos, 1:neg
- #print(loc.lcn)
- #print(extent)
- #print(direction)
- if extent==0 or not loc.is_valid:
- return loc
- else:
- offset=loc.positive_offset if direction==0 else loc.negative_offset
- if offset=="":
- return loc
- else:
- offset_loc=tmc_location(int(offset),self.tableobj)
- #return __recursion_get_extent_location(offset_loc,extent-1,direction)
- return offset_loc.get_extent_location(offset_loc,extent-1,direction)
- def __ref_locs(self,lcn,name_string=""):
- #if not self.is_valid: #not used, since not called from outside
- # return ""
- if(lcn==34196):#europe
- return(name_string)
- else:
- try:
- locarray=self.tableobj.lcl_dict[lcn]
- aref=int(locarray[6])
- loc_name=locarray[4]
- return(self.__ref_locs(aref,name_string+","+loc_name))
- #return(loc_name)
- except KeyError:
- return(name_string)
- def __str__(self):
- if not self.is_valid:
- return "invalid lcn:%i"%(self.lcn)
- elif self.ltype=="P1" and self.subtype==1:#autobahnkreuz TODO:only add if name does not already contain "Kreuz"
- name="Kreuz "+self.first_name
- elif self.ltype=="P1" and self.subtype==2:#autobahndreieck TODO:only add if name does not already contain "Dreieck"
- name="Dreieck "+self.first_name
- elif not self.roadname=="":
- name="STR:"+self.roadname#TODO remove debug
- elif not self.first_name=="":
- name=self.first_name
- else:
- name="NO NAME lcn:%i"%(self.lcn)
- return "%s,%i:%s"%(self.ltype,self.subtype,name)#no geo
- def __repr__(self):
- if not self.is_valid:
- return "invalid lcn:%i"%(self.lcn)
- #elif self.ltype[0:2] == "P1": #junction
- elif self.first_name=="":#no first name-> use aref name
- name=self.aref
- else:
- name=self.roadname+","+self.first_name
- if self.has_koord:
- return "%s,%i:%s, geo:%s"%(self.ltype,self.subtype,name,self.koord_str)
- #return '%s,%i:%s, geo:%s '%(self.ltype,self.subtype,name,self.google_maps_link,self.koord_str)
- else:
- return "%s,%i:%s"%(self.ltype,self.subtype,name)
- #if self.ltype[0]=="A":#area
- #return "%s:%s"%(self.ltype,self.first_name)
- #elif self.ltype[0]=="L":#line
- #return "%s:%s"%(self.ltype,self.first_name)
- #elif self.ltype[0]=="P":#point
- #return "%s:%s"%(self.ltype,self.first_name)
- def __init__(self,lcn,tableobj):
- self.tableobj=tableobj
- self.reflocs=self.__ref_locs(lcn)
- self.lcn=lcn
- self.has_koord=False
- self.linRef=None
- self.is_valid=False
- try:
- loc_array=tableobj.lcl_dict[lcn]
- self.ltype=loc_array[0]
- try:
- self.subtype=int(loc_array[1])
- except ValueError:#should not happen, all rows have int
- self.subtype=0
- print("location subtype %s is invalid in location %i"%(loc_array[1],lcn))
- self.roadnumber=loc_array[2]
- self.roadname=loc_array[3]
- self.first_name=loc_array[4]
- self.second_name=loc_array[5]
- if not loc_array[7]=="":
- self.linRef=tmc_location(int(loc_array[7]),tableobj)
- self.negative_offset=loc_array[8]
- self.positive_offset=loc_array[9]
- try:
- #koords stored in WGS84 format with decimal degrees multiplied with 10^5
- self.xkoord=int(loc_array[27])/100000.0
- self.ykoord=int(loc_array[28])/100000.0
- self.koord_str="%f,%f"%(self.ykoord,self.xkoord)
- self.koord_str_google="{lat: %f, lng: %f}"%(self.ykoord,self.xkoord)
- self.google_maps_link="https://www.google.de/maps/place/%f,%f"%(self.ykoord,self.xkoord)
- self.has_koord=True
- except ValueError:
- self.has_koord=False
- self.is_valid=True
- if not lcn==34196:#Europe does not have an area reference
- self.aref=tmc_location(int(loc_array[6]),tableobj)
- except KeyError:
- #print("location '%i' not found"%lcn)
- self.is_valid=False
- ##LOCATIONCODE;TYPE;SUBTYPE;ROADNUMBER;ROADNAME;FIRST_NAME;SECOND_NAME;AREA_REFERENCE;LINEAR_REFERENCE;NEGATIVE_OFFSET;POSITIVE_OFFSET;URBAN;INTERSECTIONCODE;INTERRUPTS_ROAD;IN_POSITIVE;OUT_POSITIVE;IN_NEGATIVE;OUT_NEGATIVE;PRESENT_POSITIVE;PRESENT_NEGATIVE;EXIT_NUMBER;DIVERSION_POSITIVE;DIVERSION_NEGATIVE;VERÄNDERT;TERN;NETZKNOTEN_NR;NETZKNOTEN2_NR;STATION;X_KOORD;Y_KOORD;POLDIR;ADMIN_County;ACTUALITY;ACTIVATED;TESTED;SPECIAL1;SPECIAL2;SPECIAL3;SPECIAL4;SPECIAL5;SPECIAL6;SPECIAL7;SPECIAL8;SPECIAL9;SPECIAL10
-
-class tmc_dict:
- "dict of tmc messages sorted by location (LCN) and update class, automatically deletes/updates invalid(ated) items"
- marker_template="addMarker({loc},'{text}',{endloc})"
- def __init__(self):
- self.messages={}
- self.message_list=[]
- def add(self,message):
- self.message_list.append(message)
- try:
- lcn=message.location.lcn
- updateClass=message.event.updateClass
- if not self.messages.has_key(lcn):
- self.messages[lcn]={}
- if message.event.is_cancellation:
- try:
- self.messages[lcn][updateClass].cancellation_time=message.getTime()#cancellation_time = rx time of cancellation message
- except KeyError:#no message to cancel
- message.event.name="no message to cancel"
- self.messages[lcn][updateClass]=message
- else:
- self.messages[lcn][updateClass]=message
- #print("added message: "+str(message))
- except AttributeError:
- print("ERROR, not adding: "+str(message))
- def matchFilter(self,msg,filters):
- if not msg.location.is_valid:
- return True#always show invalid messages
- loc_str=str(msg.location)+str(msg.location.reflocs)+str(msg.location.roadnumber)
-
-
- for f in filters:#filters is list of dicts {"type":"event","str":"Stau"}
- stringlist=f["str"].lower().split(";")
- for string in stringlist:
- if f["type"]=="event" and unicode(str(msg.event), encoding="UTF-8").lower().find(string)==-1:#if event filter does not match
- return False
- elif f["type"]=="location" and unicode(loc_str, encoding="UTF-8").lower().find(string)==-1:#if location filter does not match
- return False
- return True
- def getLogString(self,filters):
- retStr=""
- for message in self.message_list:
- if self.matchFilter(message,filters):
- retStr+=message.log_string()
- retStr+="\n"
- retStr+=message.multi_str()
- retStr+="\n"
- return retStr
- def getMarkerString(self):
- markerstring=""
- for lcn in self.messages:
- loc=None
- endloc=None
- map_tag=''
- for updateClass in self.messages[lcn]:
- message=self.messages[lcn][updateClass]
- if message.cancellation_time==None:
- color="black"
- else:
- color="gray"
- if message.location.has_koord:
- if loc==None:#first message at this location
- map_tag+='
'
- map_tag+=message.location_text()
- map_tag+=' '
- if message.cancellation_time==None:
- endloc=message.end_loc()#line displays length of 1st message (lowest class), that is not cancelled
- loc=message.location
- map_tag+=''%color
- map_tag+=message.map_string()
- map_tag+=' '
- map_tag+='
'
- map_tag+='
'
- if not loc==None:
- if endloc==None or not endloc.is_valid:
- endloc=loc#creates line of 0 length (dot)
- markerstring+=tmc_dict.marker_template.format(loc=loc.koord_str_google,text=map_tag,endloc=endloc.koord_str_google)
- markerstring+="\n"
- return markerstring
-
-
-class tmc_message:
- #Nature Information or Silent Forecast
-#Duration Type Dynamic Longer lasting Dynamic Longer lasting
- #0 (none) (none) (none) (none)
- #1 for at least for at least next within the within the next few
- #next 15 min few hours next 15 min hours
- #2 for at least for the rest of the within the later today
- #next 30 min day next 30 min
- #3 for at least until tomorrow within the tomorrow
- #next 1 h evening next 1 h
- #4 for at least for the rest of the within the the day after tomorrow
- #next 2 h week next 2 h
- #5 for at least until the end of within the this weekend
- #next 3 h next week next 3 h
- #6 for at least until the end of within the later this week
- #next 4 h the month next 4 h
- #7 for the rest of for a long period within the of next week
- #the day the day
- duration_dict={"Info_dyn":["","for at least next 15 min","for at least next 30 min","for at least next 1 h","for at least next 2 h","for at least next 3 h","for at least next 4 h","for the rest of the day"],
- "Info_long":["","for at least next few hours","for the rest of the day","until tomorrow evening","for the rest of the week","until the end of next week","until the end of the month","for a long period"],
- "Forecast_dyn":["","within the next 15 min","within the next 30 min","within the next 1 h","within the next 2 h","within the next 3 h","within the next 4 h","within the day"],
- "Forecast_long":["","within the next few hours","later today","tomorrow","the day after tomorrow","this weekend","later this week","next week"]}
- #Nature Information or Silent Forecast
-#Duration Type Dynamic Longer lasting Dynamic Longer lasting
-#0 15 min 1h 15 min 1h
-#1 15 min 2h 15 min 2h
-#2 30 min until midnight 30 min until midnight
-#3 1h until midnight 1h until midnight next day
-# next day
-#4 2h until midnight 2h until midnight next day
-# next day
-#5 3h until midnight 3h until midnight next day
-# next day
-#6 4h until midnight 4h until midnight next day
-# next day
-#7 until midnight until midnight until midnight until midnight next day
-# next day
- persistence_dict={"dyn":[0.25,0.25,0.5,1,2,3,4,"m"],
- "long":[1,2,"m","nm","nm","nm","nm","nm"]}
- #m=midnight, nm=midnight nex day, same for forecast and info/silent
- #datetime.timedelta(hours=0.25)
- def getDuration(self):#returns string
- if "D" in self.event.durationType and not self.event.nature=="F":
- return ", "+tmc_message.duration_dict["Info_dyn"][self.tmc_DP]
- elif "L" in self.event.durationType and not self.event.nature=="F":
- return ", "+tmc_message.duration_dict["Info_long"][self.tmc_DP]
- elif "D" in self.event.durationType and self.event.nature=="F":
- return ", "+tmc_message.duration_dict["Forecast_dyn"][self.tmc_DP]
- elif "L" in self.event.durationType and self.event.nature=="F":
- return ", "+tmc_message.duration_dict["Forecast_long"][self.tmc_DP]
- else:
- return ""
- #self.event.durationType #D,(D),L,(L)
- #self.event.nature# "",S,F
- def getPersistance(self):#returns timedelta
- persistence_dict=tmc_message.persistence_dict
- try:
- if "D" in self.event.durationType:
- return timedelta(hours=persistence_dict["dyn"][self.tmc_DP])
- if "L" in self.event.durationType:
- return timedelta(hours=persistence_dict["long"][self.tmc_DP])
- except TypeError:
- print("ERROR: TODO line 354")
- def __copy__(self):#doesn't copy, tmc_messages dont change if complete
- return self
- def __deepcopy__(self,memo):#return self, because deep copy fails
- return self
- def __hash__(self):#unused
- if self.is_single:
- return self.tmc_hash
- else:
- return self.ci
- def multi_str(self):
- if self.is_single:
- multi="[single]"
- else:
- try:
- multi="%i:%s"%(self.length,str(self.mgm_list))
- except AttributeError:
- multi="[multi incomplete]"
- return str(multi)
- def info_str(self):
- info=""
- info+=self.getDuration()
- if not self.cancellation_time==None:
- if language=="de":
- info+=" (aufgehoben um %s)"%self.cancellation_time
- else:
- info+=" (cancelled at %s)"%self.cancellation_time
- return info
- def events_string(self):
- str_list=[str(elem) for elem in self.events]
- return str(", ".join(str_list))
- def log_string(self):
- return str(self.event.updateClass)+": "+self.getTime()+": "+self.location_text()+": "+self.events_string()+"; "+self.info_str()+"; "+self.psn
- def db_string(self):
- return str(self.location)+": "+str(self.event.updateClass)+": "+self.events_string()+"; "+self.info_str()
- def map_string(self):
- return ''%self.multi_str()+str(self.event.updateClass)+": "+self.getTime()+": "+self.events_string()+self.info_str()+"; "+self.psn+" "
- def end_loc(self):
- return self.location.get_extent_location(self.location,self.tmc_extent,self.tmc_dir)
- def location_text(self):
- text=str(self.location)#use __str__ of location if no location_text implemented
- #TODO add "dreieck" for P1.2 -> done in tmc_message.__str__
- if not self.location.linRef==None:#test
- #self.tmc_extent and self.tmc_dir are ints
- #offset_loc=self.location.get_extent_location(self.location,self.tmc_extent,self.tmc_dir)
- offset_loc=self.end_loc()
- if offset_loc.is_valid:
- #offset_loc_name=str(offset_loc)
- offset_loc_name=offset_loc.first_name
- else:
- print(offset_loc)
- offset_loc_name="###INVALID###"
- templates={"de_1":"{A}, {B} in Richtung {C}"#codeing handbook: zwischen {D} und {E}, sprachdurchsagen: zwischen {E} und {D}
- ,"de_2a":", zwischen {D} und {E}"
- ,"de_2b":", bei {D}"#extent==0
- ,"en_1":"{A}, {B} {C}"
- ,"en_2a":", between {D} and {E}"
- ,"en_2b":", at {D}"}#extent==0
- text=templates[language+"_1"].format(A=self.location.linRef.roadnumber, B=self.location.linRef.second_name,C=self.location.linRef.first_name)
- if self.location.first_name==offset_loc_name:#similar to self.tmc_extent==0 (but some similar location have same same name)
- text+=templates[language+"_2b"].format(D=self.location.first_name)
- else:
- text+=templates[language+"_2a"].format(D=self.location.first_name,E=offset_loc_name)
-
- #LocCode: RefLine: RoadNr
- #A
- #LocCode:RefLine:Name2
- #B
- #LocCode:RefLine:Name1
- #C
- #LocCode:Name1
- #D
- #LocCode:Extent:OffsNeg:Name1
- #E
- #EventCode: EventText
- #F
- return str(text)
- def __str__(self):
- return str(self.event.updateClass)+": "+self.getTime()+": "+self.events_string()+"; "+self.multi_str()
- def __repr__(self):
- #event_name=self.ecl_dict[self.tmc_event][1]
- #message_string="TMC-message,event:%s location:%i,reflocs:%s, station:%s"%(event_name,self.tmc_location,self.ref_locs(self.tmc_location,""),self.RDS_data[PI]["PSN"])
- return "single:%i,complete:%i,event:%i location:%s"%(self.is_single,self.is_complete,self.event.ecn,self.location)
- def getTime(self):#always returns string
- if self.hastime:
- return self.datetime_received.strftime("%H:%M")
- else:
- return "88:88"
- def __init__(self,PI,tmc_x,tmc_y,tmc_z,datetime_received,tableobj):#TODO handle out of sequence data
- self.psn=tableobj.RDS_data[PI]["PSN"]
- #check LTN
- try:
- msg_ltn=tableobj.RDS_data[PI]["AID_list"][52550]["LTN"]
- table_ltn=1#german table
- if msg_ltn != table_ltn and tableobj.debug and False:#disabled, spams log
- print("msg_ltn:%i does not match expected table (1) on station: %s"%(msg_ltn,self.psn))
- except KeyError:
- if tableobj.debug:
- print("no LTN found")
- #self.time_received=time_received
- self.datetime_received=datetime_received
- if self.datetime_received==None:
- self.hastime=False
- else:
- self.hastime=True
- self.debug_data=""
- self.tableobj=tableobj
- self.PI=PI
- #self.isCancelled=False
- self.cancellation_time=None
- self.tmc_hash=hash((PI,tmc_x,tmc_y,tmc_z))
- tmc_T=tmc_x>>4 #0:TMC-message 1:tuning info/service provider name
- assert tmc_T==0, "this is tuning info and no alert_c message"
- Y15=int(tmc_y>>15)
- tmc_F=int((tmc_x>>3)&0x1) #identifies the message as a Single Group (F = 1) or Multi Group (F = 0)
- self.is_single=(tmc_F==1)
- self.is_multi=(tmc_F==0)
- if self.is_single or (self.is_multi and Y15==1):#single group or 1st group of multigroup
- if self.is_single:
- self.tmc_D=Y15 #diversion bit(Y15)
- self.tmc_DP=int(tmc_x&0x7) #duration and persistence 3 bits
- self.is_complete=True
- else:#1st group of multigroup -> no diversion bit, no duration (sent in mgm_tags)
- self.is_complete=False
- self._second_group_received=False
- self.tmc_D=0
- self.tmc_DP=0#default to duration of 0, can be changed with MGM
- self.ci=int(tmc_x&0x7) #continuity index
- self.data_arr=BitArray()
- self.mgm_list=[]
- self.location=tmc_location(tmc_z,tableobj)
- self.tmc_location=self.location#decrepated
- #self.event=int(tmc_y&0x7ff) #Y10-Y0
- self.event=tmc_event(int(tmc_y&0x7ff),self.tableobj) #Y10-Y0
- self.events=[self.event]
- #try:
- #self.event_name = self.tableobj.ecl_dict[self.event][1]
- #except KeyError:
- #self.event_name="##Error##"
- self.tmc_extent=int((tmc_y>>11)&0x7) #3 bits (Y13-Y11)
- self.tmc_dir=int((tmc_y>>14)&0x1) #+-direction bit (Y14)
- if not self.event.is_valid:
- print("invalid main event")
- print(self)
- else:#subsequent groups in multigroup -> Y0..Y11 and Z0..Z15 are special format
- raise ValueError, "subsequent groups must be added to existing tmc message"
- tableobj.tmc_messages.add(self)
- def add_group(self,tmc_y,tmc_z):
- sg=int((tmc_y>>14)&0x1)#=1 if second group Y14
- gsi=int((tmc_y>>12)&0x3)#group sequence indicator Y12..13 ,max length:5
- if sg==1 and not self._second_group_received:#if second group
- self.length=gsi
- self.count=self.length
- self._second_group_received=True #prevents duplicate second group from resetting counters
- try:
- if self.count==gsi: #group in sequence
- data1=int(tmc_y&0xfff)#data block 1
- data2=int(tmc_z)#data block 2
-
- self.data_arr.append("0x%03X"%data1)#3 hex characters
- self.data_arr.append("0x%04X"%data2)#4 hex characters
- #print(gsi)
-
- if self.count==0:#last group
- self.is_complete=True
- self.debug_data=copy.deepcopy(self.data_arr)
- last_event=self.event
- while len(self.data_arr)>4:#decode mgm
- label=self.data_arr[0:4].uint
- del self.data_arr[0:4]
- fieldlen=mgm_tag.field_lengths[label]
- data=self.data_arr[0:fieldlen]
- del self.data_arr[0:fieldlen]
- if not (label==0 and data.uint ==0):#ignore trailing zeros
- self.mgm_list.append(mgm_tag(label,data,self.tableobj))
- if label==0:#duration/persistence
- self.tmc_DP=data.uint
- #label==1: control codes
- elif label==1 and data.uint==2:
- last_event.change_directionality#change directionality
- elif label==1 and data.uint==5:
- self.tmc_D=1#set diversion bit
- elif label==1 and data.uint==6:
- self.tmc_extent+=8#increase extent
- elif label==1 and data.uint==7:
- self.tmc_extent+=16#increase extent
-
- elif label==2:#length
- last_event.add_length(data)
- elif label==3:#speed
- last_event.add_speed(data)
- elif label==4:#5 bit quantifier
- last_event.add_quantifier(data,5)
- elif label==5:#8 bit quantifier
- last_event.add_quantifier(data,8)
- elif label==9:#additional event
- last_event=tmc_event(data.uint,self.tableobj)
- if not last_event.is_valid:
- print("invalid MGM event")
- self.events.append(last_event)
-
-
- self.count-=1
- except AttributeError:
- #3rd or later group receiver before second
- #print("out of sequence")
- pass
-
-class mgm_tag:#mgm=multi group message
- field_lengths=[3, 3, 5, 5, 5, 8, 8, 8, 8, 11, 16, 16, 16, 16, 0, 0]
- field_names={0:"Duration (value 000 is not allowed)"
- ,1:"Control code."
- ,2:"Length of route affected."
- ,3:"Speed limit advice."
- ,4:"quantifier (5 bit field)"
- ,5:"quantifier (8 bit field)"
- ,6:"Supplementary information code."
- ,7:"Explicit start time (or time when problem was reported) for driver information only."
- ,8:"Explicit stop time for driver information and message management."
- ,9:"Additional event."
- ,10:"Detailed diversion instructions."
- ,11:"Destination."
- ,12:"Reserved for future use"
- ,13:"Cross linkage to source of problem , on another route."
- ,14:"Content Separator."
- ,15:"Reserved for future use."}
- control_codes={0:"Default urgency increased by one level."
- ,1: "Default urgency reduced by one level."
- ,2:" Default directionality of message changed."
- ,3:" Default 'dynamic' or 'longer-lasting' provision interchanged."
- ,4:" Default spoken or unspoken duration interchanged."
- ,5:" Equivalent of diversion bit set to '1'."
- ,6:" Increase the number of steps in the problem extent by 8."
- ,7:" Increase the number of steps in the problem extent by 16."}
- control_codes_short={0:"urgency+=1"
- ,1:" urgency-=1"
- ,2:" directionality changed"
- ,3:" dynamic/longer-lasting changed"
- ,4:" spoken/unspoken duration changed"
- ,5:" diversion=1"
- ,6:" extent+=8"
- ,7:" extent+=16"}
- @staticmethod
- def decode_time_date(raw):#label7/8 raw to datestring
- if raw<=95:
- hrs=int(raw/4)#takes floor
- mns=(95%4)*15
- return "%i:%i"%(hrs,mns)
- elif raw<=200:#hour and day
- return "%i hours"%(raw-96)
- elif raw<=231:#day of month
- return "%s of month"%ordinal(raw-200)
- elif raw<=255:#months
- return "%s months"%((raw-231)/2.0)
- else:
- raise ValueError, "label7/8 time must be between 0 and 255"
- @staticmethod
- def length_to_km(raw):#label2 raw to km
- if raw==0:
- return 100
- elif raw <=10:
- return raw
- elif raw <=15:
- return 2*raw-10
- elif raw <=31:
- return 5*raw-55
- else:
- raise ValueError, "label2-length must be between 0 and 31"
-
- def __repr__(self):
- try:
- if(self.label==0):
- return "duration: %i"%self.data.uint
- elif(self.label==1):
- return "control code %i: %s"%(self.data.uint,mgm_tag.control_codes_short[self.data.uint])
- elif(self.label==2):
- return "length affected: %i km"%self.length_to_km(self.data.uint)
- elif(self.label==3):
- return "speed limit: %i km/h"%(self.data.uint*5)
- elif(self.label==4):
- return "5 bit quantifier: %i"%(self.data.uint)
- elif(self.label==5):
- return "8 bit quantifier: %i"%(self.data.uint)
- elif(self.label==6):
- return "info:%s"%self.tableobj.label6_suppl_info[self.data.uint]
- elif(self.label==7):
- return "start: %s"%self.decode_time_date(self.data.uint)
- elif(self.label==8):
- return "stop: %s"%self.decode_time_date(self.data.uint)
- elif(self.label==9):
- event=tmc_event(self.data.uint,self.tableobj)
- #event_string="event: %s"%self.tableobj.ecl_dict[self.data.uint][1]
- #return event_string
- return "event: %s"%event.name
- elif(self.label==10):
- #location_string="divert via: %s"%",".join(self.tableobj.lcl_dict[self.data.uint][3:5])#roadname(col3) and firstname (col4)
- location_string="divert via: %s"%tmc_location(self.data.uint,self.tableobj)
- return location_string
- elif(self.label==11):
- location_string="dest.: %s"%tmc_location(self.data.uint,self.tableobj)
- return location_string
- elif(self.label==13):
- location_string="crosslink: %s"%tmc_location(self.data.uint,self.tableobj)
- return location_string
- else:
- return "%i:%s"%(self.label,str(self.data))
- except KeyError:
- return "%i:%s"%(self.label,str(self.data))
-
- def __init__(self,label,data,tableobj):
- self.tableobj=tableobj
- assert 0<=label and label <16,"mgm-tag label has to be between 0 and 15"
- self.label = label
- self.data = data
- def label_string(self):
- return field_names[self.label]
-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):#START
- """
- docstring for block qtguitest
- """
- def goodbye(self):
- self.clean_data_and_commit_db()
- print("quitting rds parser table, closing db")
- if self.writeDB:
- #self.db.commit()
- self.db.close()
- def __init__(self,signals,nPorts,slot,freq,log,debug,workdir,writeDB):
- #QObject.__init__()
- gr.sync_block.__init__(self,
- name="RDS Table",
- in_sig=None,
- out_sig=None)
- if nPorts==1:
- self.message_port_register_in(pmt.intern('in'))
- self.set_msg_handler(pmt.intern('in'), functools.partial(self.handle_msg, port=0))
- else:
- for i in range(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.nPorts=nPorts
- self.message_port_register_in(pmt.intern('freq'))
- self.set_msg_handler(pmt.intern('freq'), self.set_freq)
- self.message_port_register_out(pmt.intern('ctrl'))
- self.log=log
- self.debug=debug
- self.writeDB=writeDB
- self.signals=signals
- self.RDS_data={}
- self.change_freq_tune=slot
- self.tuning_frequency=int(freq)
- self.printcounter=0
- self.ODA_application_names={}
- self.TMC_data={}
- self.IH_data={}
- self.decoder_frequencies={}
- self.decoders=[]
- for i in range(nPorts):
- self.decoders.append({'synced':False,'freq':None})
- #self.decoder_synced={}
- #self.colorder=['ID','freq','name','PTY','AF','time','text','quality','buttons']
- self.colorder=['ID','freq','name','buttons','PTY','AF','time','text','quality','RT+']
- self.workdir=workdir
- self.PI_dict={}#contains PI:numpackets (string:integer)
- self.tmc_messages=tmc_dict()
-
- if self.writeDB:
- #create new DB file
- db_name=workdir+'RDS_data'+datetime.now().strftime("%Y%m%d_%H%M%S")+'.db'
- db=sqlite3.connect(db_name, check_same_thread=False)
- self.db=db
- #create tables
- try:
- db.execute('''CREATE TABLE stations
- (PI text PRIMARY KEY UNIQUE,PSN text, freq real, PTY text,TP integer)''')
- db.execute('''CREATE TABLE groups
- (time text,PI text,PSN text, grouptype text,content blob)''')
- db.execute('''CREATE TABLE data
- (time text,PI text,PSN text, dataType text,data blob)''')
- db.execute('''CREATE TABLE grouptypeCounts
- (PI text,grouptype text,count integer,unique (PI, grouptype))''')
- db.execute('''CREATE TABLE TMC
- (hash text PRIMARY KEY UNIQUE,time text,PI text, F integer,event integer,location integer,DP integer,div integer,dir integer,extent integer,text text,multi text,rawmgm text)''')
- db.commit()
-
- except sqlite3.OperationalError:
- print("ERROR: tables already exist")
-
- #self.dbc.execute('''CREATE TABLE rtp
- # (time text,PI text,rtp_string text)''')
- reader = csv.reader(open(self.workdir+'RDS_ODA-AIDs_names_only.csv'), delimiter=',', quotechar='"')
- reader.next()#skip header
- for row in reader:
- self.ODA_application_names[int(row[0])]=row[1]
- #read location code list:
- reader = csv.reader(open(self.workdir+'LCL15.1.D-160122_utf8.csv'), delimiter=';', quotechar='"')
- reader.next()#skip header
- self.lcl_dict=dict((int(rows[0]),rows[1:]) for rows in reader)
- #read RT+ class name list:
- reader = csv.reader(open(self.workdir+'RTplus_classnames.csv'), delimiter=',', quotechar='"')
- reader.next()#skip header
- self.rtp_classnames=dict((int(rows[0]),rows[1]) for rows in reader)
- #read TMC-event list
- reader = csv.reader(open(self.workdir+'event-list_with_forecast_sort.csv'), delimiter=',', quotechar='"')
- reader.next()#skip header
- self.ecl_dict=dict((int(rows[0]),rows[1:]) for rows in reader)
- #Code,Text CEN-English,Text (German),Text (German) kein Quantifier,Text (Quantifier = 1),Text (Quantifier >1),N,Q,T,D,U,C,R ,Comment
- #N:nature (blank): information, F:forecast, S:silent
- #Q:quantifier type: (0..12) or blank (no quantifier)
- #T:duration type: D:dynamic, L:long lasting, in brackets or if time-of-day quantifier (no 7) is used in message -> no display, only for management
- #D:direction: 1:unidirectional, 2:bidirectional
- #U:urgency: blank: normal, X:extremely urgent, U:urgent
- #C: update class:
-
- #read update classes
- reader = csv.reader(open(self.workdir+'tmc_update_class_names.csv'), delimiter=',', quotechar='"')
- reader.next()#skip header, "code(C),english,german"
- if language=="de":
- self.tmc_update_class_names=dict((int(rows[0]),rows[2]) for rows in reader)#german names
- else:
- self.tmc_update_class_names=dict((int(rows[0]),rows[1]) for rows in reader)#english names
- #read supplementary information code list
- reader = csv.reader(open(self.workdir+'label6-supplementary-information-codes.csv'), delimiter=',', quotechar='"')
- reader.next()#skip header, "code,english,german"
- if language=="de":
- self.label6_suppl_info=dict((int(rows[0]),rows[2]) for rows in reader)#german
- else:
- self.label6_suppl_info=dict((int(rows[0]),rows[1]) for rows in reader)#english
- #read PTY list
- f=open(self.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()
- self.minute_count=0
- self.minute_count_max=0
- self.minute_count_timer=time.time()
- self.save_data_timer=time.time()
-
- atexit.register(self.goodbye)
-
- def clean_data_and_commit_db(self):
- for PI in self.PI_dict:
- self.PI_dict[PI]-=1
- #print(self.PI_dict)
- if self.writeDB:
- self.db.commit()
- f=open(self.workdir+'google_maps_markers.js', 'w')
- markerstring=self.tmc_messages.getMarkerString()
- markerstring+='\n console.log("loaded "+markers.length+" markers")'
- markerstring+='\n document.getElementById("errorid").innerHTML = "loaded "+markers.length+" markers";'
- f.write(markerstring)
- f.close()
- def update_freq(self):
- # " " is a tab character
- message_string="decoder frequencies:"
- for num in self.decoder_frequencies:
- freq=self.decoder_frequencies[num]
- if self.decoders[num]['synced']:
- message_string+=" %i:%0.1fM "% (num,freq/1e6)
- #print("'color:green'>%i:%0.1fM"% (num,freq/1e6))
- else:#elif self.decoders[num]['synced']==False:
- #print("'color:red'>%i:%0.1fM"% (num,freq/1e6))
- message_string+=" %i:%0.1fM "% (num,freq/1e6)
- message_string+=" tuned frequency:%0.1fM"%(self.tuning_frequency/1e6)
- self.signals.DataUpdateEvent.emit({'decoder_frequencies':message_string})
- #print(message_string)
- #self.signals.DataUpdateEvent.emit({'row':decoder_num,'freq':freq_str})
- #print("nr:%i freq:%s"%(tgtnum,freq_str))
- def set_freq_tune(self,freq):
- self.tuning_frequency=int(freq)
- self.update_freq()
- def set_freq(self,msg):
- m = pmt.symbol_to_string(msg)
- decoder_num=int(m.split()[0])-1#msgs are 1-indexed, decoder_num is 0-indexed
- freq_str=m.split()[1]
- try:
- freq=float(freq_str)
- self.decoder_frequencies[decoder_num]=freq
- freq_str="%i:%0.1fM"% (decoder_num,freq/1e6)
- except ValueError:
- pass#leave string as is
- self.update_freq()
- def init_data_for_PI(self,PI):
- self.RDS_data[PI]={}
- #self.RDS_data[PI]["blockcounts"]={}# no defaults (works aswell)
- #defaults are to keep colors in piechart consistent between stations:
- self.RDS_data[PI]["blockcounts"]={"0A":0,"1A":0,"2A":0,"3A":0,"4A":0,"6A":0,"8A":0,"12A":0,"14A":0}
- 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"]={"set":set(),"rxset":set()}
- self.RDS_data[PI]["TP"]=-1
- self.RDS_data[PI]["TA"]=-1
- self.RDS_data[PI]["PTY"]=""
- self.RDS_data[PI]["DI"]=[2,2,2,2]
- self.RDS_data[PI]["internals"]={"last_rt_tooltip":"","unfinished_TMC":{},"last_valid_rt":"","last_valid_psn":"","RT_history":[]}
- self.RDS_data[PI]["time"]={"timestring":"88:88","datestring":"00-00-0000","datetime":None}
- def handle_msg(self, msg, port):#port from 0 to 3
- if pmt.to_long(pmt.car(msg))==1L:
- data=pmt.to_python(pmt.cdr(msg))
- #print("port:%i, data: %s"%(port,data))
- self.decoders[port]['synced']=data
- self.update_freq()
- else: #elif pmt.to_long(pmt.car(msg))==0L
- array=pmt.to_python(msg)[1]
-
- if time.time()-self.save_data_timer > 10:#every 10 seconds
- self.save_data_timer=time.time()
- self.clean_data_and_commit_db()
-
- if time.time()-self.minute_count_timer > 3:#every 3 second
- self.minute_count_max=self.minute_count
- self.signals.DataUpdateEvent.emit({'group_count':self.minute_count,'group_count_max':self.minute_count_max})
- self.minute_count=0
- self.minute_count_timer=time.time()
- #pr.enable()#disabled-internal-profiling
- self.minute_count+=1
- #self.signals.DataUpdateEvent.emit({'group_count':self.minute_count,'group_count_max':self.minute_count_max})
- if self.writeDB:
- #db=sqlite3.connect(self.db_name)
- db=self.db
-
-
- groupNR=array[2]&0b11110000
- groupVar=array[2]&0b00001000
- if (groupVar == 0):
- groupType=str(groupNR >> 4)+"A"
- else:
- groupType=str(groupNR >> 4)+"B"
- #if self.debug:
- #PI=str(port)+"_%02X%02X" %(array[0],array[1])
- #else:
- #PI="%02X%02X" %(array[0],array[1])
- 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])
-
- try:
- self.PI_dict[PI]+=1
- except KeyError:
- pass
-
- if not self.RDS_data.has_key(PI):#station invalid/new
- if not self.PI_dict.has_key(PI):#1st group
- self.PI_dict[PI]=1
- return#dont decode further if not yet valid
- elif self.PI_dict[PI]>2:#count station as valid if more than 2 packets received
- self.init_data_for_PI(PI)#initialize dict for station
- if self.log:
- print("found station %s"%PI)
- else:
- return#dont decode further if not yet valid
-
- if self.decoder_frequencies.has_key(port):
- freq=self.decoder_frequencies[port]
- freq_str="%i:%0.1fM"% (port,freq/1e6)
- self.RDS_data[PI]["tuned_freq"]=freq
- #self.signals.DataUpdateEvent.emit({'PI':PI,'freq':freq_str})
- if self.RDS_data[PI]["blockcounts"].has_key(groupType):
- self.RDS_data[PI]["blockcounts"][groupType] +=1 #increment
- else:
- self.RDS_data[PI]["blockcounts"][groupType] = 1 #initialize (1st group of this type)
- self.RDS_data[PI]["blockcounts"]["any"]+=1
- if self.RDS_data[PI]["blockcounts"]["any"]==5:
- self.RDS_data[PI]["blockcounts"]["any"]=0
- t=(str(PI),groupType,self.RDS_data[PI]["blockcounts"][groupType])#TODO only update DB every few seconds
- if self.writeDB:
- db.execute("INSERT OR REPLACE INTO grouptypeCounts (PI,grouptype,count) VALUES (?,?,?)",t)
- dots="."*self.RDS_data[PI]["blockcounts"]["any"]
- self.RDS_data[PI]["TP"]=TP
- self.RDS_data[PI]["PTY"]=self.pty_dict[PTY]
-
- self.signals.DataUpdateEvent.emit({'row':port,'PI':PI,'PTY':self.pty_dict[PTY],'TP':TP,'wrong_blocks':wrong_blocks,'dots':dots})
-
-
-
- #add any received groups to DB (slow)
- #content="%02X%02X%02X%02X%02X" %(array[3]&0x1f,array[4],array[5],array[6],array[7])
- #t=(str(datetime.now()),PI,self.RDS_data[PI]["PSN"],groupType,content)
- #db.execute("INSERT INTO groups VALUES (?,?,?,?,?)",t)
-
- 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#decoder information
- #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
- self.RDS_data[PI]["TA"]=TA
- #style='font-family:Courier New;color:%s'
- flag_string="TP:%i, TA:%i, MS:%i, DI:%s "%(TP,TA,MS,str(self.RDS_data[PI]["DI"]))
- pty_colored=self.RDS_data[PI]["PTY"]
- if TP==1:
- if TA==1:
- color="red"
- elif TA==0:
- color="green"
- else:
- color="yellow"
- pty_colored="%s "%(color,self.RDS_data[PI]["PTY"])
-
- self.signals.DataUpdateEvent.emit({'row':port,'PI':PI,'flags':flag_string,'PTY':pty_colored})
-
- #224 1110 0000 = no AF
- #225 1110 0001 = 1AF
- #249 1111 1001 = 25AF
- fillercode=205#1100 1101
- if not self.RDS_data[PI]["AF"].has_key("main") and self.RDS_data[PI].has_key("tuned_freq"):
- #if self.RDS_data[PI].has_key("tuned_freq"):#update main freq even if one exists -> DB problem
- freq=self.decode_AF_freq(array[4])
- if freq==self.RDS_data[PI]["tuned_freq"]:
- self.RDS_data[PI]["AF"]["main"]=freq
- if self.log:
- print("main frequency found in 0A: station:%s, freq:%0.1fM"% (self.RDS_data[PI]["PSN"],freq/1e6))
- freq_str="0A:%0.1fM"% (freq/1e6)
- self.signals.DataUpdateEvent.emit({'PI':PI,'freq':freq_str})
- t=(PI,self.RDS_data[PI]["PSN"],float(freq),self.RDS_data[PI]["PTY"],int(self.RDS_data[PI]["TP"]))
- if self.writeDB:
- db.execute("INSERT INTO stations (PI,PSN,freq,PTY,TP) VALUES (?,?,?,?,?)",t)
- freq=self.decode_AF_freq(array[5])
- if freq==self.RDS_data[PI]["tuned_freq"]:
- self.RDS_data[PI]["AF"]["main"]=freq
- if self.log:
- print("main frequency found in 0A: station:%s, freq:%0.1fM"% (self.RDS_data[PI]["PSN"],freq/1e6))
- freq_str="0A:%0.1fM"% (freq/1e6)
- self.signals.DataUpdateEvent.emit({'PI':PI,'freq':freq_str})
- t=(PI,self.RDS_data[PI]["PSN"],float(freq),self.RDS_data[PI]["PTY"],int(self.RDS_data[PI]["TP"]))
- if self.writeDB:
- db.execute("INSERT INTO stations (PI,PSN,freq,PTY,TP) VALUES (?,?,?,?,?)",t)
- if self.RDS_data[PI].has_key("tuned_freq") :#TODO add secondary freqs
- freq=self.decode_AF_freq(array[4])
- diff=abs(freq-self.RDS_data[PI]["tuned_freq"])
- if diff<100000:
- self.RDS_data[PI]["AF"]["rxset"].add(freq)
- freq=self.decode_AF_freq(array[5])
- diff=abs(freq-self.RDS_data[PI]["tuned_freq"])
- if diff<100000:
- self.RDS_data[PI]["AF"]["rxset"].add(freq)
-
- if(array[4]>= 224 and array[4]<= 249):
- #print("AF1 detected")
- self.RDS_data[PI]["AF"]['number']=array[4]-224
- #self.RDS_data[PI]["AF"]['main']=self.decode_AF_freq(array[5])
- self.signals.DataUpdateEvent.emit({'row':port,'PI':PI,'AF':self.RDS_data[PI]["AF"]})
- if(array[5]>= 224 and array[5]<= 249):
- print("AF2 detected (shouldn't happen) %s"%array[5])
-
-
- #add frequencies to set
- self.RDS_data[PI]["AF"]["set"].add(self.decode_AF_freq(array[4]))
- self.RDS_data[PI]["AF"]["set"].add(self.decode_AF_freq(array[5]))
- try:
- self.RDS_data[PI]["AF"]["set"].remove(0)#remove control characters
- except KeyError:
- pass
-
- name_list=list(self.RDS_data[PI]["PSN"])
- 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.RDS_data[PI]["PSN"]="_"*8
- self.RDS_data[PI]["PSN_valid"]=[False]*8
- self.RDS_data[PI]["PSN_valid"][adr*2:adr*2+2]=[True] *2
- self.RDS_data[PI]["PSN"]="".join(name_list)
- #determine if text is valid
- valid=True
- for i in range(0,8):
- if (not self.RDS_data[PI]["PSN_valid"][i]):
- valid = False
- if(valid):
- #textcolor="black"
- textcolor=""#use default color (white if background is black)
- if not self.RDS_data[PI]["internals"]["last_valid_psn"]==self.RDS_data[PI]["PSN"]:#ignore duplicates
- t=(str(datetime.now()),PI,self.RDS_data[PI]["PSN"],"PSN_valid",self.RDS_data[PI]["PSN"])
- if self.writeDB:
- db.execute("INSERT INTO data (time,PI,PSN,dataType,data) VALUES (?,?,?,?,?)",t)
- t=(self.RDS_data[PI]["PSN"],PI)
- if self.writeDB:
- db.execute("UPDATE OR IGNORE stations SET PSN=? WHERE PI IS ?",t)
- self.RDS_data[PI]["internals"]["last_valid_psn"]=self.RDS_data[PI]["PSN"]
- else:
- textcolor="gray"
- formatted_text=self.color_text(self.RDS_data[PI]["PSN"],adr*2,adr*2+2,textcolor,segmentcolor)
- self.signals.DataUpdateEvent.emit({'row':port,'PI':PI,'PSN':formatted_text})
- elif (groupType == "1A"):#PIN programme item number
- #wer nutzt 1A gruppen?
- #antenne1: variants: 0(ECC),
- PIN=(array[6]<<8)|(array[7])
- SLC=(array[4]<<8)|(array[5])&0xfff#slow labeling code
- radio_paging=array[3]&0x1f
- LA=array[4]>>7#linkage actuator
- variant=(array[4]>>4)&0x7
- PIN_day=(PIN>>11)&0x1f
- PIN_hour=(PIN>>6)&0x1f
- PIN_minute=PIN&0x3f
- PIN_valid= PIN_day in range(1,32) and PIN_hour in range(0,24) and PIN_minute in range(0,60)
- if PIN_valid:
- self.RDS_data[PI]["PIN"]=[PIN_day,PIN_hour,PIN_minute]
- data_string="variant:%i,SLC:%04X,PIN (valid):%s "%(variant,SLC,str([PIN_day,PIN_hour,PIN_minute]))
- else:
- data_string="variant:%i,SLC:%04X,PIN:%04X "%(variant,SLC,PIN)
- #%02X%02X%02X%02X%02X
-
- t=(str(datetime.now()),PI,self.RDS_data[PI]["PSN"],"PIN",data_string)
- if self.writeDB:
- db.execute("INSERT INTO data (time,PI,PSN,dataType,data) VALUES (?,?,?,?,?)",t)
- if self.debug and not variant==0:#print if not seen before
- print("PI:%s PSN:%s uses variant %i of 1A"%(PI,self.RDS_data[PI]["PSN"],variant))
- if variant==0:
- paging=array[4]&0xf
- extended_country_code=array[5]
- self.RDS_data[PI]["ECC"]=extended_country_code
- if self.debug:
- print("PI:%s PSN:%s,ECC:%s"%(PI,self.RDS_data[PI]["PSN"],hex(extended_country_code)))
- elif variant==1:
- TMC_info=SLC
- elif variant==2:
- paging_info=SLC
- elif variant==3:
- language_codes=SLC
- if self.debug:
- print("PI:%s PSN:%s,language_codes:%s"%(PI,self.RDS_data[PI]["PSN"],hex(language_codes)))
- elif variant==6:
- #for use by broadcasters
- if self.debug:
- print("PI:%s PSN:%s uses variant 6 of 1A"%(PI,self.RDS_data[PI]["PSN"]))
- elif variant==7:
- ESW_channel_identification=SLC
- #end of 1A decode
- elif (groupType == "2A"):#RT radiotext
- if(not self.RDS_data[PI].has_key("RT_0")):#initialize variables
- self.RDS_data[PI]["RT_0"]={"RT":"_"*64,"RT_valid":[False]*64,"RT_all_valid":False}
- self.RDS_data[PI]["RT_1"]={"RT":"_"*64,"RT_valid":[False]*64,"RT_all_valid":False}
- #self.RDS_data[PI]["RT"]="_"*64
- #self.RDS_data[PI]["RT_valid"]=[False]*64
- #self.RDS_data[PI]["RT_all_valid"]=False
- self.RDS_data[PI]["RT_last_ab_flag"]=2
-
- adr= array[3]&0b00001111
- ab_flag=(array[3]&0b00010000)>>4
- #print("PI:%s, AB:%i"%(PI,ab_flag))
-
-
- #if self.RDS_data[PI]["RT_last_ab_flag"] !=ab_flag:#AB flag changed -> clear text
- # self.RDS_data[PI]["RT"]="_"*64
- # self.RDS_data[PI]["RT_valid"]=[False]*64
- # self.RDS_data[PI]["RT_last_ab_flag"] =ab_flag
- self.RDS_data[PI]["RT_last_ab_flag"] =ab_flag
-
- 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.RDS_data[PI]["RT_"+str(ab_flag)]["RT"])
- #determine text length:
- try:
- text_end=text_list.index('\r')
- except ValueError:
- text_end=64 #assume whole string is important
- pass
- predicted=False
- 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.RDS_data[PI]["RT_"+str(ab_flag)]["RT"]="_"*64
- self.RDS_data[PI]["RT_"+str(ab_flag)]["RT_valid"]=[False]*64
- #predict RT from last texts:
- for rt in self.RDS_data[PI]["internals"]["RT_history"]:
- if rt[adr*4:adr*4+4]==list(segment):
- self.RDS_data[PI]["RT_"+str(ab_flag)]["RT"]="".join(rt)
- predicted=True
-
- self.RDS_data[PI]["RT_"+str(ab_flag)]["RT_valid"][adr*4:adr*4+4]=[True] *4
- if not predicted:
- self.RDS_data[PI]["RT_"+str(ab_flag)]["RT"]="".join(text_list)
-
- #determine if (new) text is valid
- self.RDS_data[PI]["RT_"+str(ab_flag)]["RT_all_valid"]=True
- for i in range(0,text_end):
- if (not self.RDS_data[PI]["RT_"+str(ab_flag)]["RT_valid"][i]):
- self.RDS_data[PI]["RT_"+str(ab_flag)]["RT_all_valid"] = False
- if(self.RDS_data[PI]["RT_"+str(ab_flag)]["RT_all_valid"]):
- #textcolor="black"
- textcolor=""#use default color (white if background is black)
- l=list(self.RDS_data[PI]["RT_"+str(ab_flag)]["RT"])
- rt="".join(l[0:text_end])#remove underscores(default symbol) after line end marker
- if not self.RDS_data[PI]["internals"]["last_valid_rt"]==rt:#ignore duplicates #TODO add 2nd order duplicates ABAB
- self.RDS_data[PI]["internals"]["RT_history"].append(l)
- if len(self.RDS_data[PI]["internals"]["RT_history"])>10:#only store last 10 RTs
- self.RDS_data[PI]["internals"]["RT_history"].pop(0)
- t=(str(datetime.now()),PI,self.RDS_data[PI]["PSN"],"RT",rt)
- if self.writeDB:
- db.execute("INSERT INTO data (time,PI,PSN,dataType,data) VALUES (?,?,?,?,?)",t)
- self.RDS_data[PI]["internals"]["last_valid_rt"]=rt
- try:#save rt+ if it exist
- if self.writeDB:
- t=(str(datetime.now()),PI,self.RDS_data[PI]["PSN"],"RT+",str(self.RDS_data[PI]["RT+"]))
- db.execute("INSERT INTO data (time,PI,PSN,dataType,data) VALUES (?,?,?,?,?)",t)
- except KeyError:
- pass#no rt+ -> dont save
- else:
- textcolor="gray"
- formatted_text=self.color_text(self.RDS_data[PI]["RT_"+str(ab_flag)]["RT"],adr*4,adr*4+4,textcolor,segmentcolor)
- rtcol=self.colorder.index('text')
- self.signals.DataUpdateEvent.emit({'col':rtcol,'row':port,'PI':PI,'string':formatted_text})
-
- elif (groupType == "3A"):#ODA announcements (contain application ID "AID")
- AID=(array[6]<<8)|(array[7])#combine 2 bytes into 1 block
- app_data=(array[4]<<8)|(array[5])#content defined by ODA-app
- app_group_raw=array[3]&0x1f #group type in which this app is sent
- if (app_group_raw&0x1 == 0):
- app_group=str(app_group_raw >> 1)+"A"
- else:
- app_group=str(app_group_raw >> 1)+"B"
-
- if not self.RDS_data[PI]["AID_list"].has_key(AID):#new ODA found
- try:
- app_name=self.ODA_application_names[AID]
- except KeyError:
- if self.debug:
- print("ERROR: ODA-app-id (AID) '%i' not found in list on station %s, app group:%s"%(AID,app_group,PI))
- app_name="unknown"
- self.RDS_data[PI]["AID_list"][AID]={}
- self.RDS_data[PI]["AID_list"][AID]["groupType"]=app_group
- self.RDS_data[PI]["AID_list"][AID]["app_name"]=app_name
- self.RDS_data[PI]["AID_list"][AID]["app_data"]=app_data
- if AID==52550:#TMC alert-c initialize
- self.RDS_data[PI]["AID_list"][AID]["provider name"]="________"
- if self.log:
- print("new ODA: AID:%i, name:'%s', app_group:%s, station:%s" %(AID,app_name,app_group,PI))
- #decode 3A group of TMC
- if AID==52550:#TMC alert-c (continuously update)
- variant=app_data>>14
- if variant==0:
- self.RDS_data[PI]["AID_list"][AID]["LTN"]=(app_data>>6)&0x3f#location table number (6 bits)
- self.RDS_data[PI]["AID_list"][AID]["AFI"]=(app_data>>5)&0x1#alternative frequency indicator
- self.RDS_data[PI]["AID_list"][AID]["M"]=(app_data>>4)&0x1#transmission mode indicator
- #Message Geographical Scope:
- self.RDS_data[PI]["AID_list"][AID]["scope"]=""
- if (app_data>>3)&0x1==1:
- self.RDS_data[PI]["AID_list"][AID]["scope"]+="I"#international (EUROROAD)
- if (app_data>>2)&0x1==1:
- self.RDS_data[PI]["AID_list"][AID]["scope"]+="N"#national
- if (app_data>>1)&0x1==1:
- self.RDS_data[PI]["AID_list"][AID]["scope"]+="R"#regional
- if (app_data>>0)&0x1==1:
- self.RDS_data[PI]["AID_list"][AID]["scope"]+="U"#urban
- #self.RDS_data[PI]["AID_list"][AID]["I"]=(app_data>>3)&0x1#international (EUROROAD)
- #self.RDS_data[PI]["AID_list"][AID]["N"]=(app_data>>2)&0x1#national
- #self.RDS_data[PI]["AID_list"][AID]["R"]=(app_data>>1)&0x1#regional
- #self.RDS_data[PI]["AID_list"][AID]["U"]=(app_data>>0)&0x1#urban
- elif variant==1:
- self.RDS_data[PI]["AID_list"][AID]["SID"]=(app_data>>6)&0x3f#service identifier
- #timing parameters (used to switch away from TMC station without missing messages):
- self.RDS_data[PI]["AID_list"][AID]["G"]=(app_data>>12)&0x3#gap parameter
- self.RDS_data[PI]["AID_list"][AID]["activity_time"]=(app_data>>4)&0x3
- self.RDS_data[PI]["AID_list"][AID]["window_time"]=(app_data>>2)&0x3
- self.RDS_data[PI]["AID_list"][AID]["delay_time"]=(app_data>>0)&0x3
- elif self.debug:
- print("unknown variant %i in TMC 3A group"%variant)
- elif (groupType == "4A"):#CT clock time
- bits=BitArray('uint:8=%i,uint:8=%i,uint:8=%i,uint:8=%i,uint:8=%i'%tuple(array[3:8]))
- spare,datecode,hours,minutes,offsetdir,local_time_offset = bits.unpack("uint:6,uint:17,uint:5,uint:6,uint:1,uint:5")
- local_time_offset*=0.5
- #datecode=((array[3] & 0x03) << 15) | (array[4] <<7)|((array[5] >> 1) & 0x7f)#modified julian date
- if datecode==0:
- #do not update!!
- if self.debug:
- print("station:%s sent empty 4A group"%self.RDS_data[PI]["PSN"])
- else:
- #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
-
- date=datetime(1858,11,17)+timedelta(days=int(datecode))#convert from MJD (modified julian date)
-
- timestring="%02i:%02i (%+.1fh)" % (hours,minutes,local_time_offset)
- datestring=date.strftime("%d.%m.%Y")
- ctcol=self.colorder.index('time')
- self.signals.DataUpdateEvent.emit({'col':ctcol,'row':port,'PI':PI,'string':timestring,'tooltip':datestring})
- t=(str(datetime.now()),PI,self.RDS_data[PI]["PSN"],"CT",datestring+" "+timestring+"; datecode(MJD):"+str(datecode))
- self.RDS_data[PI]["time"]["timestring"]=timestring
- self.RDS_data[PI]["time"]["datestring"]=datestring
- try:
- self.RDS_data[PI]["time"]["datetime"]=datetime(date.year,date.month,date.day,hours,minutes)+timedelta(hours=local_time_offset)
- except ValueError:
- print("ERROR: could not interpret time or date:"+datestring+" "+timestring)
- if self.writeDB:
- db.execute("INSERT INTO data (time,PI,PSN,dataType,data) VALUES (?,?,?,?,?)",t)
- elif (groupType == "6A"):#IH inhouse data -> save for analysis
- """In House Data:
-{'130A': {'1E1077FFFF': {'count': 1,
- 'last_time': '2016-12-08 16:26:54.767596'},
- '1F23022015': {'count': 1,
- 'last_time': '2016-12-08 16:26:56.341271'}},
- 'D00F': {'1E1023FFFF': {'count': 1,
- 'last_time': '2016-12-08 16:26:54.769165'},
- '1F28032008': {'count': 3,
- 'last_time': '2016-12-08 16:26:58.272420'}}}"""
- ih_data="%02X%02X%02X%02X%02X" %(array[3]&0x1f,array[4],array[5],array[6],array[7])
- if not self.IH_data.has_key(PI):
- self.IH_data[PI]={}
- if not self.IH_data[PI].has_key(ih_data):
- self.IH_data[PI][ih_data]={}
- self.IH_data[PI][ih_data]["count"]=0
- self.IH_data[PI][ih_data]["count"]+=1
- self.IH_data[PI][ih_data]["last_time"]=str(datetime.now())
- #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 alert-C
- tmc_x=array[3]&0x1f #lower 5 bit of block2
- tmc_y=(array[4]<<8)|(array[5]) #block3
- tmc_z=(array[6]<<8)|(array[7])#block4
- tmc_hash=md5.new(str([PI,tmc_x,tmc_y,tmc_z])).hexdigest()
- tmc_T=tmc_x>>4 #0:TMC-message 1:tuning info/service provider name
- tmc_F=int((tmc_x>>3)&0x1) #identifies the message as a Single Group (F = 1) or Multi Group (F = 0)
- Y15=int(tmc_y>>15)
- #timestring=self.RDS_data[PI]["time"]["timestring"]
- datetime_received=self.RDS_data[PI]["time"]["datetime"]
- if tmc_T == 0:
- if tmc_F==1:#single group
- tmc_msg=tmc_message(PI,tmc_x,tmc_y,tmc_z,datetime_received,self)
- self.print_tmc_msg(tmc_msg)
- elif tmc_F==0 and Y15==1:#1st group of multigroup
- ci=int(tmc_x&0x7)
- tmc_msg=tmc_message(PI,tmc_x,tmc_y,tmc_z,datetime_received,self)
- #if self.RDS_data[PI]["internals"]["unfinished_TMC"].has_key(ci):
- #print("overwriting parital message")
- self.RDS_data[PI]["internals"]["unfinished_TMC"][ci]={"msg":tmc_msg,"time":time.time()}
- else:
- ci=int(tmc_x&0x7)
- if self.RDS_data[PI]["internals"]["unfinished_TMC"].has_key(ci):
- tmc_msg=self.RDS_data[PI]["internals"]["unfinished_TMC"][ci]["msg"]
- tmc_msg.add_group(tmc_y,tmc_z)
- age=time.time()-self.RDS_data[PI]["internals"]["unfinished_TMC"][ci]["time"]
- t=(time.time(),PI,age,ci,tmc_msg.is_complete)
- #print("%f: continuing message PI:%s,age:%f,ci:%i complete:%i"%t)
- self.RDS_data[PI]["internals"]["unfinished_TMC"][ci]["time"]=time.time()
- if tmc_msg.is_complete:
- self.print_tmc_msg(tmc_msg)#print and store message
- del self.RDS_data[PI]["internals"]["unfinished_TMC"][tmc_msg.ci]#delete finished message
- else:
- #if not ci==0:
- #print("ci %i not found, discarding"%ci)
- pass
-
- else:#alert plus or provider info
- adr=tmc_x&0xf
- if 4 <= adr and adr <= 9:
- #seen variants 4569, 6 most often
- #print("TMC-info variant:%i"%adr)
- if adr==4 or adr==5:#service provider name
- segment=self.decode_chars(chr(array[4])+chr(array[5])+chr(array[6])+chr(array[7]))
- if self.debug:
- print("TMC-info adr:%i (provider name), segment:%s, station:%s"%(adr,segment,self.RDS_data[PI]["PSN"]))
- if self.RDS_data[PI]["AID_list"].has_key(52550):
- text_list=list(self.RDS_data[PI]["AID_list"][52550]["provider name"])
- seg_adr_start=(adr-4)*4#start of segment
- text_list[seg_adr_start:seg_adr_start+4]=segment
- self.RDS_data[PI]["AID_list"][52550]["provider name"]="".join(text_list)
- if adr== 7:#freq of tuned an mapped station (not seen yet)
- freq_TN=tmc_y>>8
- freq_ON=tmc_y&0xff#mapped frequency
- if self.debug:
- print("TMC-info: TN:%i, station:%s"%(freq_TN,self.RDS_data[PI]["PSN"]))
- self.RDS_data[PI]["TMC_TN"]=freq_TN
- else:
- if self.debug:
- print("alert plus on station %s (%s)"%(PI,self.RDS_data[PI]["PSN"]))#(not seen yet)
-
- #self.tableobj.RDS_data["D301"]["AID_list"][52550]["provider name"]="test____"
- #RadioText+ (grouptype mostly 12A):
- elif self.RDS_data[PI]["AID_list"].has_key(19415) and self.RDS_data[PI]["AID_list"][19415]["groupType"]==groupType:#RT+
- if not self.RDS_data[PI].has_key("RT+"):
- #self.RDS_data[PI]["RT+"]={"history":{},"last_item_toggle_bit":2}
- self.RDS_data[PI]["RT+"]={"last_item_toggle_bit":2}
- self.RDS_data[PI]["RT+_history"]={}
- self.RDS_data[PI]["internals"]["RT+_times"]={}
- #self.RDS_data[PI]["RT+"]["last_item_toggle_bit"]=2
- A3_data=self.RDS_data[PI]["AID_list"][19415]["app_data"]
- template_number=A3_data&0xff
- SCB=(A3_data >> 8)&0x0f#server control bit
- CB_flag=(A3_data>>12)&0x1 #is set if template available
- rtp_message= ((array[3]&0x1f)<<32)|(array[4]<<24)|(array[5]<<16)|(array[6]<<8)|(array[7])
- item_toggle_bit=(rtp_message>>36)&0x1
- item_running_bit=(rtp_message>>35)&0x1
- tag1=(rtp_message>>17)&(2**18-1)#6+6+6
- tag2=(rtp_message)&(2**17-1)#6+6+5
- tag1_type=self.rtp_classnames[int(tag1>>12)]
- tag2_type=self.rtp_classnames[int(tag2>>11)]
- tag1_start=int((tag1>>6)&(2**6-1))
- tag1_len=int(tag1&(2**6-1))
- tag2_start=int((tag2>>5)&(2**6-1))
- tag2_len=int(tag2&(2**5-1))
- if not self.RDS_data[PI]["RT+"]["last_item_toggle_bit"] == item_toggle_bit: #new item
- #self.RDS_data[PI]["RT+"]["history"][str(datetime.now())]=self.RDS_data[PI]["internals"]["last_rt_tooltip"]
- t=(str(datetime.now()),PI,self.RDS_data[PI]["PSN"],"RT+",str(self.RDS_data[PI]["RT+"]))
- if self.writeDB:
- db.execute("INSERT INTO data (time,PI,PSN,dataType,data) VALUES (?,?,?,?,?)",t)
- self.RDS_data[PI]["RT+_history"][str(datetime.now())]=copy.deepcopy(self.RDS_data[PI]["RT+"])#save old item
- self.RDS_data[PI]["RT+"]["last_item_toggle_bit"] = item_toggle_bit
- rtcol=self.colorder.index('text')
- if self.debug:
- print("toggle bit changed on PI:%s, cleared RT-tt"%PI)
- self.signals.DataUpdateEvent.emit({'col':rtcol,'row':port,'PI':PI,'tooltip':""})
- if self.RDS_data[PI].has_key("RT_0"):
- ab_flag=self.RDS_data[PI]["RT_last_ab_flag"]
- rt=self.RDS_data[PI]["RT_"+str(ab_flag)]["RT"]
- rt_valid=self.RDS_data[PI]["RT_"+str(ab_flag)]["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]
- self.RDS_data[PI]["internals"]["RT+_times"][tag1_type]=time.time()
- 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]
- self.RDS_data[PI]["internals"]["RT+_times"][tag2_type]=time.time()
- #check outdated tags:
- for tagtype in self.RDS_data[PI]["internals"]["RT+_times"].keys():#.keys() makes copy to avoid RuntimeError: dictionary changed size during iteration
- age=time.time()-self.RDS_data[PI]["internals"]["RT+_times"][tagtype]
- if age>90:#delete if older than 90 sek#TODO delete if toggle bit changes?, delete if tag changes? (title change -> delete artist)
- del self.RDS_data[PI]["internals"]["RT+_times"][tagtype]
- del self.RDS_data[PI]["RT+"][tagtype]
-
-
- tags="ir:%i,it:%i"%(item_running_bit,item_toggle_bit)
- rtpcol=self.colorder.index('RT+')
- self.signals.DataUpdateEvent.emit({'col':rtpcol,'row':port,'PI':PI,'string':tags})
- if(tag2_type=="ITEM.TITLE" and self.RDS_data[PI].has_key("RT_0")):#TODO remove duplicate code
- ab_flag=self.RDS_data[PI]["RT_last_ab_flag"]
- rt=self.RDS_data[PI]["RT_"+str(ab_flag)]["RT"]
- rt_valid=self.RDS_data[PI]["RT_"+str(ab_flag)]["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')
- #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))
- elif (groupType == "14A"):#EON enhanced other networks
- #TN = tuned network, ON=other network
- if not self.RDS_data[PI].has_key("EON"):
- self.RDS_data[PI]["EON"]={}
- TP_ON=(array[3]>>4)&0x1
- PI_ON="%02X%02X" %(array[6],array[7])
- variant=array[3]&0xf
- self.signals.DataUpdateEvent.emit({'PI':PI_ON,'TP':TP_ON})
- if not self.RDS_data.has_key(PI_ON):
- self.init_data_for_PI(PI_ON)
- self.RDS_data[PI_ON]["TP"]=TP_ON
- self.PI_dict[PI_ON]=0#initialize dict, even if no packets received
- if self.log:
- print("found station %s via EON on station %s"%(PI_ON,PI))
- if not self.RDS_data[PI]["EON"].has_key(PI_ON):
- self.RDS_data[PI]["EON"][PI_ON]={}
- self.RDS_data[PI]["EON"][PI_ON]["PSN"]="_"*8
- if variant in range(4):#variant 0..3 -> PS_ON
- segment=self.decode_chars(chr(array[4])+chr(array[5]))
- name_list=list(self.RDS_data[PI_ON]["PSN"])
- #name_list=list(self.RDS_data[PI]["EON"][PI_ON]["PSN"])
- name_list[variant*2:variant*2+2]=segment
- if (name_list[variant*2:variant*2+2]==list(segment)):#segment already there
- segmentcolor="purple"
- elif(name_list[variant*2:variant*2+2]==['_']*2): #segment new
- segmentcolor="purple"
- name_list[variant*2:variant*2+2]=segment
- else:#name changed (böse)
- segmentcolor="red"
- name_list=['_']*8 #reset name
- name_list[variant*2:variant*2+2]=segment
- #reset stored text:
- self.RDS_data[PI_ON]["PSN"]="_"*8
- self.RDS_data[PI_ON]["PSN_valid"]=[False]*8
- self.RDS_data[PI_ON]["PSN_valid"][variant*2:variant*2+2]=[True] *2
- PS_ON_str="".join(name_list)
- self.RDS_data[PI_ON]["PSN"]=PS_ON_str
- self.RDS_data[PI]["EON"][PI_ON]["PSN"]=PS_ON_str
- #determine if text is valid
- valid=True
- for i in range(0,8):
- if (not self.RDS_data[PI_ON]["PSN_valid"][i]):
- valid = False
- if(valid):
- #textcolor="black"
- textcolor=""#use default color (white if background is black)
-
- else:
- textcolor="gray"
- formatted_text=self.color_text(self.RDS_data[PI_ON]["PSN"],variant*2,variant*2+2,textcolor,segmentcolor)
- self.RDS_data[PI]["EON"][PI_ON]["PSN"]=PS_ON_str
- self.RDS_data[PI_ON]["PSN"]=PS_ON_str
- #formatted_text="%s "%("purple",PS_ON_str)
- self.signals.DataUpdateEvent.emit({'PI':PI_ON,'PSN':formatted_text})
- try:
- t=(PI_ON,self.RDS_data[PI_ON]["PSN"],float(self.RDS_data[PI_ON]["AF"]["main"]),self.RDS_data[PI_ON]["PTY"],int(self.RDS_data[PI_ON]["TP"]))
- if self.writeDB:
- db.execute("INSERT OR REPLACE INTO stations (PI,PSN,freq,PTY,TP) VALUES (?,?,?,?,?)",t)
- except KeyError:
- #not all info present -> no db update
- pass
- if variant==4:#AF_ON
- if self.debug:
- print("AF_ON method A")#TODO
- if variant in range(5,10):#variant 5..9 -> mapped freqs
- freq_TN=self.decode_AF_freq(array[4])
- freq_ON=self.decode_AF_freq(array[5])
- #lock in tuned network if freq_TN matches decoder frequency
- if(self.RDS_data[PI].has_key("tuned_freq") and freq_TN==self.RDS_data[PI]["tuned_freq"]and not self.RDS_data[PI]["AF"].has_key("main")):
- if self.log:
- print("main frequency found in 14A: station:%s, freq:%0.1fM"% (self.RDS_data[PI]["PSN"],freq_TN/1e6))
- self.RDS_data[PI]["AF"]["main"]=freq_TN
- freq_str="EON_TN:%0.1fM"% (freq_TN/1e6)
- self.signals.DataUpdateEvent.emit({'PI':PI,'freq':freq_str})
- #lock in ON if TN is locked in
- if(self.RDS_data[PI]["AF"].has_key("main") and self.RDS_data[PI]["AF"]["main"]==freq_TN and not self.RDS_data[PI_ON]["AF"].has_key("main")):
- if self.log:
- print("mapped frequency found in 14A: station:%s, freq:%0.1fM"% (self.RDS_data[PI_ON]["PSN"],freq_ON/1e6))
- self.RDS_data[PI_ON]["AF"]["main"]=freq_ON
- freq_str="EON:%0.1fM"% (freq_ON/1e6)
- self.signals.DataUpdateEvent.emit({'PI':PI_ON,'freq':freq_str})
- #print("mapped freq in variant %i:, %i->%i"%(variant,freq_TN,freq_ON))
- if variant==13:#PTY and TA of ON
- PTY_ON=array[4]>>3
- TA_ON=array[5]&0x1
- self.RDS_data[PI]["EON"][PI_ON]["TA_ON"]=TA_ON
- self.RDS_data[PI]["EON"][PI_ON]["PTY_ON"]=PTY_ON
- self.RDS_data[PI_ON]["TA"]=TA_ON
- self.RDS_data[PI_ON]["PTY"]=self.pty_dict[PTY_ON]
- self.signals.DataUpdateEvent.emit({'PI':PI_ON,'PTY':self.pty_dict[PTY_ON],'TA':TA_ON})
- #rest is reserved
- if variant==14:#programme item number of ON
- PIN_ON=(array[4]<<8)|(array[5])
- elif (groupType == "8A"):
- if self.debug:
- print("8A without 3A on PI:%s"%PI)
- #else:#other group
- if 1==1:
- #printdelay=50
- printdelay=500
- self.printcounter+=0#printing disabled
- if self.printcounter == printdelay and self.debug:
-
- for key in self.RDS_data:
- if self.RDS_data[key].has_key("PSN"):
- psn=self.RDS_data[key]["PSN"]
- else:
- psn="?"
- print("%s(%s):"%(psn,key),end="")
- pp.pprint(self.RDS_data[key]["blockcounts"])
- if self.RDS_data[key].has_key("RT+"):
- print("RT+:",end="")
- pp.pprint(self.RDS_data[key]["RT+"])
- self.printcounter=0
- #print("group of type %s not decoded on station %s"% (groupType,PI))
-
- pr.disable() #disabled-internal-profiling
- #end of handle_msg
- def print_tmc_msg(self,tmc_msg):
- try:
- PI=tmc_msg.PI
- tmc_F=tmc_msg.is_single
- tmc_hash=tmc_msg.tmc_hash
- refloc_name=""
- reflocs=tmc_msg.location.reflocs
- if not self.TMC_data.has_key(tmc_hash):#if message new
- try:
- #message_string="TMC-message,event:%s lcn:%i,location:%s,reflocs:%s, station:%s"%(str(tmc_msg.event),tmc_msg.location.lcn,tmc_msg.location,reflocs,self.RDS_data[PI]["PSN"])
- #message_string=tmc_msg.log_string()
- self.TMC_data[tmc_hash]=tmc_msg
- self.signals.DataUpdateEvent.emit({'TMC_log':tmc_msg,'multi_str':tmc_msg.multi_str()})
- #t=(str(datetime.now()),PI,self.RDS_data[PI]["PSN"],"ALERT-C",message_string.decode("utf-8"))
- #self.db.execute("INSERT INTO data (time,PI,PSN,dataType,data) VALUES (?,?,?,?,?)",t)
- timestring=self.RDS_data[PI]["time"]["timestring"]
- #message_string="%s ,locname:%s, reflocs:%s"%(str(tmc_msg.event),tmc_msg.location,reflocs)
- message_string=tmc_msg.db_string()
- t=(tmc_hash,timestring,PI, tmc_F,tmc_msg.event.ecn,int(tmc_msg.location.lcn),tmc_msg.tmc_DP,tmc_msg.tmc_D,tmc_msg.tmc_dir,tmc_msg.tmc_extent,message_string.decode("utf-8"),tmc_msg.multi_str().decode("utf-8"),str(tmc_msg.debug_data))
- if self.writeDB:
- self.db.execute("INSERT INTO TMC (hash,time,PI, F,event,location,DP,div,dir,extent,text,multi,rawmgm) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)",t)
- except Exception as e:
- print(e)
- raise
- #print("line 1064")
-
- except KeyError:
- #print("location '%i' not found"%tmc_location)
- pass
- def print_results(self):
- s = StringIO.StringIO()
- sortby = 'cumulative'
- ps = pstats.Stats(pr, stream=s).sort_stats(sortby)
- ps.print_stats()
- print(s.getvalue())
- def decode_AF_freq(self,freq_raw):
- #if freq_raw in range(1,205):#1..204 BAD coding -> memory usage
- if 1<=freq_raw <=204:
- return(87500000+freq_raw*100000)#returns int
- #return(87.5e6+freq_raw*0.1e6)#returns float
- else:
- return(0)
- def ref_locs(self,loc,name_string):
- if(loc==34196):#europe
- return(name_string)
- else:
- try:
- locarray=self.lcl_dict[loc]
- aref=int(locarray[6])
- loc_name=locarray[4]
- return(self.ref_locs(aref,name_string+","+loc_name))
- #return(loc_name)
- except KeyError:
- return(name_string)
-
- def decode_chars(self,charstring):
- alphabet={
- 0b1000:u"áàéèíìóòúùÑÇŞßiIJ",
- 0b1001:u"âäêëîïôöûüñçş??ij",
- 0b1100:u"ÁÀÉÈÍÌÓÒÚÙŘČŠŽĐĿ",
- 0b1101:u"ÂÄÊËÎÏÔÖÛÜřčšžđŀ"}
- #charlist=list(charstring)
- return_string=""
- for i,char in enumerate(charstring):
-
- if ord(char)<= 0b01111111:
- #charlist[i]=char #use ascii
- return_string+=char
- else:
- #split byte
- alnr=(ord(char)&0xF0 )>>4 #upper 4 bit
- index=ord(char)&0x0F #lower 4 bit
- try:
- #charlist[i]=alphabet[alnr][index]
- return_string+=alphabet[alnr][index]
- return_string+=unichr(ord(char))
- except KeyError:
- return_string+="?%02X?"%ord(char)
- print("symbol not decoded: "+"?%02X?"%ord(char)+"in string:"+return_string)
- #charlist[i]='?'#symbol not decoded #TODO
- pass
- #return "".join(charlist)
- return return_string
- def color_text(self, text, start,end,textcolor,segmentcolor):
- #formatted_text="%s %s %s "% (textcolor,text[:start],segmentcolor,text[start:end],textcolor,text[end:])
- #formatted_text="%s %s %s "% (textcolor,text[:start],segmentcolor,text[start:end],textcolor,text[end:])
- formatted_text=("%s "*3)% (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,tableobj,showTMC):
- #print("gui initializing")self.tableobj.RDS_data["D3A2"]
- self.signals = signals
- self.tableobj=tableobj
- self.showTMC=showTMC
- 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)#title of table disabled to save space
- #layout.addWidget(self.label)
- self.setLayout(layout)
- #self.decoder_to_PI={}
- self.PI_to_row={}
- self.table=QtGui.QTableWidget(self)
- rowcount=0
- self.table.setRowCount(rowcount)
- self.table.setColumnCount(10)
- self.table.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers) #disallow editing
-
- #self.colorder=['ID','freq','name','buttons','PTY','AF','time','text','quality']
- self.colorder=tableobj.colorder
- ##button.clicked.connect(self.getDetails)
-
- layout.addWidget(self.table)
- self.table.setHorizontalHeaderLabels(self.colorder)
- #self.table.setMaximumHeight(300)#TODO use dynamic value
-
- button_layout = Qt.QHBoxLayout()
- codebutton = QtGui.QPushButton("code.interact")
- codebutton.clicked.connect(self.onCLick)
- button_layout.addWidget(codebutton)
- ih_button = QtGui.QPushButton("show IH data")
- ih_button.clicked.connect(self.showIHdata)
- button_layout.addWidget(ih_button)
- save_button = QtGui.QPushButton("save")
- save_button.clicked.connect(self.saveData)
- button_layout.addWidget(save_button)
- print_button = QtGui.QPushButton("print profile")
- print_button.clicked.connect(self.printProfile)
- button_layout.addWidget(print_button)
- mode_button = QtGui.QPushButton("mode")
- mode_button.clicked.connect(self.switchMode)
- button_layout.addWidget(mode_button)
- layout.addLayout(button_layout)
- label_layout = Qt.QHBoxLayout()
- self.freq_label=QtGui.QLabel("decoder frequencies:")
- #self.freq_label.setTextFormat(QtCore.Qt.RichText)
- #self.freq_label.setTextFormat(QtCore.Qt.PlainText)
- self.count_label=QtGui.QLabel("count:")
- label_layout.addWidget(self.freq_label)
- label_layout.addWidget(self.count_label)
- layout.addLayout(label_layout)
- #TODO set different minsize if TMC is shown
- self.setMinimumSize(Qt.QSize(500,40*self.tableobj.nPorts))
- if self.showTMC:
- self.tmc_message_label=QtGui.QLabel("TMC messages:")
- self.event_filter=QtGui.QLineEdit()#QPlainTextEdit ?
- self.location_filter=QtGui.QLineEdit(u"Baden-Württemberg")
- #self.location_filter=QtGui.QLineEdit(u"")
- self.event_filter.returnPressed.connect(self.filterChanged)
- self.location_filter.returnPressed.connect(self.filterChanged)
-
- filter_layout = Qt.QHBoxLayout()
- filter_layout.addWidget(QtGui.QLabel("event filter:"))
- filter_layout.addWidget(self.event_filter)
- filter_layout.addWidget(QtGui.QLabel("location filter:"))
- filter_layout.addWidget(self.location_filter)
-
- layout.addLayout(filter_layout)
- layout.addWidget(self.tmc_message_label)
- self.logOutput = Qt.QTextEdit()
- self.logOutput.setReadOnly(True)
- self.logOutput.setLineWrapMode(Qt.QTextEdit.NoWrap)
- self.logOutput.setMaximumHeight(150)
- font = self.logOutput.font()
- font.setFamily("Courier")
- font.setPointSize(10)
- layout.addWidget(self.logOutput)
- self.lastResizeTime=0
- self.clip = QtGui.QApplication.clipboard()
- #self.cb.clear(mode=cb.Clipboard )
- #self.cb.setText("Clipboard Text", mode=cb.Clipboard)
- def filterChanged(self):
- print("filter changed")
- ef=unicode(self.event_filter.text().toUtf8(), encoding="UTF-8").lower()
- lf=unicode(self.location_filter.text().toUtf8(), encoding="UTF-8").lower()
- self.logOutput.clear()
- #filters=[{"type":"location", "str":u"Baden-Württemberg"}]
- filters=[{"type":"location", "str":lf},{"type":"event", "str":ef}]
- self.logOutput.append(Qt.QString.fromUtf8(self.tableobj.tmc_messages.getLogString(filters)))
- #self.logOutput.append(Qt.QString.fromUtf8(self.tableobj.tmc_messages.getLogString([])))
- def keyPressEvent(self, e):
- if (e.modifiers() & QtCore.Qt.ControlModifier) and len(self.table.selectedRanges())>0:
- selected = self.table.selectedRanges().pop()
- selected.leftColumn()
- selected.topRow()
- if e.key() == QtCore.Qt.Key_C: #copy
- try:
- qs = self.table.cellWidget(selected.topRow(),selected.leftColumn()).text()#get QString from table
- s=re.sub("<.*?>","", str(qs))#remove html tags
- self.clip.setText(s)
- except Exception as e:
- print(e)
- print("no text, cant copy")
- def insert_empty_row(self):
- rowPosition = self.table.rowCount()
- self.table.insertRow(rowPosition)
- #for col in range(self.table.columnCount()-1):#all labels except in last column -> buttons
-# self.table.setCellWidget(rowPosition,col,QtGui.QLabel())
- #initialize labels everywhere:
- for col in range(self.table.columnCount()):
- self.table.setCellWidget(rowPosition,col,QtGui.QLabel())
- button_layout = Qt.QHBoxLayout()
- details_button=QtGui.QPushButton("Detail")
- details_button.clicked.connect(functools.partial(self.getDetails, row=rowPosition))
- button_layout.addWidget(details_button)
- left_button=QtGui.QPushButton("L")
- left_button.clicked.connect(functools.partial(self.setAudio, row=rowPosition,audio_channel="left"))
- button_layout.addWidget(left_button)
- right_button=QtGui.QPushButton("R")
- right_button.clicked.connect(functools.partial(self.setAudio, row=rowPosition,audio_channel="right"))
- button_layout.addWidget(right_button)
- #self.table.setCellWidget(rowPosition,self.table.columnCount()-1,button_layout)
- cellWidget = QtGui.QWidget()
- cellWidget.setLayout(button_layout)
- #button_col=self.table.columnCount()-1
- button_col=3
- self.table.setCellWidget(rowPosition,button_col,cellWidget)
- def display_data(self, event):
- #pp.pprint(event)
- if type(event)==dict and event.has_key('group_count'):
- self.count_label.setText("count:%02i, max:%i"%(event['group_count'],event['group_count_max']))
- if type(event)==dict and event.has_key('decoder_frequencies'):
- self.freq_label.setText(event['decoder_frequencies'])
- if type(event)==dict and event.has_key('TMC_log') and self.showTMC:
- tmc_msg=event['TMC_log']
- ef=unicode(self.event_filter.text().toUtf8(), encoding="UTF-8").lower()
- lf=unicode(self.location_filter.text().toUtf8(), encoding="UTF-8").lower()
- filters=[{"type":"location", "str":lf},{"type":"event", "str":ef}]
- if self.tableobj.tmc_messages.matchFilter(tmc_msg,filters):
- self.logOutput.append(Qt.QString.fromUtf8(tmc_msg.log_string()))
- self.logOutput.append(Qt.QString.fromUtf8(tmc_msg.multi_str()))
- if type(event)==dict and event.has_key('TMC_log_str')and self.showTMC:
- ef=unicode(self.event_filter.text().toUtf8(), encoding="UTF-8").lower()
- lf=unicode(self.location_filter.text().toUtf8(), encoding="UTF-8").lower()
- text=unicode(event['TMC_log_str'], encoding="UTF-8").lower()
- if not text.find(lf)==-1 and not text.find(ef)==-1:
- self.logOutput.append(Qt.QString.fromUtf8(event['TMC_log_str']))
- if type(event)==dict and event.has_key('PI'):
- PI=event['PI']
- if not self.PI_to_row.has_key(PI):
- self.PI_to_row[PI]=len(self.PI_to_row)#zero for first PI seen, then count up
- self.insert_empty_row()
- row=self.PI_to_row[PI]
- PIcol=self.colorder.index('ID')
- self.table.cellWidget(row,PIcol).setText(PI)
-
- if event.has_key('freq'):
- freqcol=self.colorder.index('freq')
- item=self.table.cellWidget(row,freqcol)
- item.setText(event['freq'])
- if event.has_key('wrong_blocks'):
- item=self.table.cellWidget(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(row,self.colorder.index('PTY'))
- item.setText(event['PTY'])
- if event.has_key('flags'):
- item=self.table.cellWidget(row,self.colorder.index('PTY'))
- item.setToolTip(Qt.QString(event['flags']))
- if event.has_key('string'):
- item=self.table.cellWidget(row,event['col'])
- item.setText(event['string'])
- if event.has_key('tooltip'):
- item=self.table.cellWidget(row,event['col'])
- item.setToolTip(Qt.QString(event['tooltip']))
- if event.has_key('AF'):
- #setAF
- PIcol=self.colorder.index('AF')
- self.table.cellWidget(row,PIcol).setText(str(event['AF']['number']))
- if event.has_key('PSN'):
- #setPSN
- PSNcol=self.colorder.index('name')
- item=self.table.cellWidget(row,PSNcol)
- item.setText(event['PSN'])
- if time.time()-self.lastResizeTime > 2:#every 2 seconds
- self.table.resizeColumnsToContents()
- self.lastResizeTime=time.time()
- #end of display-data
- def printProfile(self):
- self.tableobj.print_results()
- def switchMode(self):
- #print("mode switch message sent")
- send_pmt = pmt.pmt_to_python.pmt_from_dict({"cmd":"switch mode"})
- #send_pmt = pmt.string_to_symbol("switch mode")
- self.tableobj.message_port_pub(pmt.intern('ctrl'), send_pmt)
- def saveData(self):
- filename="RDS_data_"+str(datetime.now())+".txt"
- f=open(self.tableobj.workdir+filename,"w")
- rds_data=copy.deepcopy(self.tableobj.RDS_data)
- for PI in sorted(rds_data):
- try:
- del rds_data[PI]['PSN_valid']
- del rds_data[PI]['RT_valid']
- except KeyError:
- pass
- f.write("Data:%s"%pp.pformat(rds_data))
- f.write("\n\nIn House Data:\n%s"%pp.pformat(self.tableobj.IH_data))
- f.close()
- print("data saved in file %s"%filename)
- def showIHdata(self):
- view=Qt.QDialog()
- l=QtGui.QLabel("In House Data:\n%s"%pp.pformat(self.tableobj.IH_data))
- l.setTextInteractionFlags(QtCore.Qt.TextSelectableByMouse |QtCore.Qt.TextSelectableByKeyboard)
- l.setWordWrap(True)
- #self.IH_data
- layout=Qt.QVBoxLayout()
- layout.addWidget(l)
- view.setLayout(layout)
- view.exec_()
- def setAudio(self,row,audio_channel):
-
- PIcol=self.colorder.index('ID')
- PI=str(self.table.cellWidget(row,PIcol).text())
- freq=int(self.tableobj.RDS_data[PI]['AF']['main'])
- #print("setaudio row:%i, chan:%s, PI:%s,freq:%i"%(row,audio_channel,PI,freq))
- send_pmt = pmt.pmt_to_python.pmt_from_dict({"cmd":"set_audio_freq","chan":audio_channel,"freq":freq})
- self.tableobj.message_port_pub(pmt.intern('ctrl'), send_pmt)
- #catch:
- #print("no freq, cant set decoder")#show notification? popup: too intrusive, log: maybe not visible, other possibility?
- #print("freq not in RX BW")#automatically shift freq-tune?
- def getDetails(self,row):
- PIcol=self.colorder.index('ID')
- PI=str(self.table.cellWidget(row,PIcol).text())
- view = chart.DialogViewer()
- if self.tableobj.PI_dict.has_key(PI) and self.tableobj.PI_dict[PI]>3:#dont print piechart if no packets received (detected via EON)
- table=chart.DataTable()
- table.addColumn('groupType')
- table.addColumn('numPackets')
- blockcounts=copy.deepcopy(self.tableobj.RDS_data[PI]['blockcounts'])
- 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.setGraph(mychart)
- #view.resize(360, 240)
- #view.resize(380, 550)
- rds_data=copy.deepcopy(self.tableobj.RDS_data[PI])
- try:
- del rds_data['blockcounts']
- del rds_data['PSN_valid']
- del rds_data["RT_0"]['RT_valid']
- del rds_data["RT_1"]['RT_valid']
- rds_data['internals']['RT_history']=["".join(rt) for rt in rds_data['internals']['RT_history']]#combine char lists into strings (more compact)
- except KeyError:
- pass
- l=QtGui.QLabel("Data:%s"%pp.pformat(rds_data))
- l.setTextInteractionFlags(QtCore.Qt.TextSelectableByMouse |QtCore.Qt.TextSelectableByKeyboard)
- l.setWordWrap(True)
- #l=QtGui.QLabel("Data:")
-
- #view.layout().addWidget(l)
-
- scrollArea = QtGui.QScrollArea(self)
- scrollArea.setWidgetResizable(True)
- scrollArea.setWidget(l)
- view.layout().addWidget(scrollArea)
- view.setWindowTitle(self.tableobj.RDS_data[PI]["PSN"])
- view.exec_()
- def onCLick(self):
- print("button clicked")
- code.interact(local=locals())
-if __name__ == "__main__":
- from PyQt4 import Qt
- import sys
-
-
- app = Qt.QApplication(sys.argv)
- 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)
- sys.exit(app.exec_())
-
- widget = None
-
-