From ebf406f84fd38c1bbd77e7cf33adccf55755f16e Mon Sep 17 00:00:00 2001
From: csrichter
Date: Mon, 16 Jan 2017 21:48:04 +0100
Subject: [PATCH] timedelta calculations, message cancellation, extent
calculation, message-sentence generation, tmc dict, new map marker
generation, duration/persistance displayed/calculated, writeDB option, PTY
red on traffic announcement (TA==1), fixed date calculation (MJD form 4A),
table reordered,Left/right play button, decoder lock-feature , scrollable
detail view
---
grc/CMakeLists.txt | 3 +-
grc/crfa_rds_parser_table_qt.xml | 18 +-
grc/crfa_stream_selector.xml | 38 ++
lib/rds_decoder_impl.cc | 4 +-
python/CMakeLists.txt | 3 +-
python/__init__.py | 12 +-
python/max_freq.py | 45 ++-
python/rds_parser_table_qt.py | 598 ++++++++++++++++++++++++-------
python/rds_parser_table_qt.pyc | Bin 0 -> 38980 bytes
python/stream_selector.py | 87 +++++
10 files changed, 656 insertions(+), 152 deletions(-)
create mode 100644 grc/crfa_stream_selector.xml
create mode 100644 python/rds_parser_table_qt.pyc
create mode 100644 python/stream_selector.py
diff --git a/grc/CMakeLists.txt b/grc/CMakeLists.txt
index e298d86..8d24022 100644
--- a/grc/CMakeLists.txt
+++ b/grc/CMakeLists.txt
@@ -23,5 +23,6 @@ install(FILES
crfa_rds_parser_table_qt.xml
crfa_rds_decoder.xml
crfa_max_freq.xml
- crfa_smooth_vectors.xml DESTINATION share/gnuradio/grc/blocks
+ crfa_smooth_vectors.xml
+ crfa_stream_selector.xml DESTINATION share/gnuradio/grc/blocks
)
diff --git a/grc/crfa_rds_parser_table_qt.xml b/grc/crfa_rds_parser_table_qt.xml
index 734b985..97e4670 100644
--- a/grc/crfa_rds_parser_table_qt.xml
+++ b/grc/crfa_rds_parser_table_qt.xml
@@ -12,7 +12,7 @@
#set $label = '"%s"'%$id
#end if
$(signals) = rds_parser_table_qt_Signals()
-self.$(id) = crfa.rds_parser_table_qt($(signals),$nPorts,self.set_$(freq_tune),$freq_tune,$log, $debug,$workdir)
+self.$(id) = crfa.rds_parser_table_qt($(signals),$nPorts,self.set_$(freq_tune),$freq_tune,$log, $debug,$workdir,$writeDB)
$(win) = rds_parser_table_qt_Widget($signals, $label,self.$(id))
$(gui_hint()($win))
set_freq_tune($freq_tune);
@@ -78,7 +78,21 @@ $(gui_hint()($win))
False
-
+
+ write Database
+ writeDB
+ False
+ bool
+
+
+
+
+
+
+ in_re
+ float
+
+
+ in_arg
+ float
+
+
+ rds_re
+ message
+ 1
+
+
+ rds_arg
+ message
+ 1
+
+
+ rds_out
+ message
+ 1
+
+
diff --git a/lib/rds_decoder_impl.cc b/lib/rds_decoder_impl.cc
index 7bacaa5..cfec273 100644
--- a/lib/rds_decoder_impl.cc
+++ b/lib/rds_decoder_impl.cc
@@ -175,9 +175,9 @@ int rds_decoder_impl::work (int noutput_items,
if (block_bit_counter<25) block_bit_counter++;
else {
good_block=false;
- dataword=(reg>>10) & 0xffff;
+ dataword=(reg>>10) & 0xffff;//data part of received block (upper 16 bits)
block_calculated_crc=calc_syndrome(dataword,16);
- checkword=reg & 0x3ff;
+ checkword=reg & 0x3ff;//checkword part of received block (lower 10 bits)
/* manage special case of C or C' offset word */
if (block_number==2) {
block_received_crc=checkword^offset_word[block_number];
diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt
index a2dae5d..5adfc50 100644
--- a/python/CMakeLists.txt
+++ b/python/CMakeLists.txt
@@ -37,7 +37,8 @@ GR_PYTHON_INSTALL(
rds_parser_table_qt.py
max_freq.py
smooth_vectors.py
- chart.py DESTINATION ${GR_PYTHON_DIR}/crfa
+ chart.py
+ stream_selector.py DESTINATION ${GR_PYTHON_DIR}/crfa
)
########################################################################
diff --git a/python/__init__.py b/python/__init__.py
index e42dcda..5eb27b8 100644
--- a/python/__init__.py
+++ b/python/__init__.py
@@ -24,13 +24,12 @@ description here (python/__init__.py).
'''
# import swig generated symbols into the crfa namespace
-#try:
- ## this might fail if the module is python-only
- #from crfa_swig import *
-#except ImportError:
- #pass
+try:
+ # this might fail if the module is python-only
+ from crfa_swig import *
+except ImportError:
+ pass
-from crfa.crfa_swig import *
# import any pure python here
from multi_rds_printer import multi_rds_printer
from qtguitest import qtguitest
@@ -39,4 +38,5 @@ from rds_parser_table_qt import rds_parser_table_qt
from max_freq import max_freq
from smooth_vectors import smooth_vectors
from chart import Chart
+from stream_selector import stream_selector
#
diff --git a/python/max_freq.py b/python/max_freq.py
index 130f74b..34b9f95 100644
--- a/python/max_freq.py
+++ b/python/max_freq.py
@@ -44,18 +44,46 @@ class max_freq(gr.sync_block):
self.message_port_register_in(pmt.intern('ctrl'))
self.set_msg_handler(pmt.intern('ctrl'), self.handle_ctrl_msg)
self.searchMode=True
+ self.index_fixed=[False]*self.num_decoders
+ def freq_to_index(self,freq):
+ startfreq=self.center_freq-self.samp_rate/2
+ #freq=self.samp_rate*index/self.fft_len+startfreq
+ #(freq-startfreq)*self.fft_len/self.samp_rate=index
+ index=(freq-startfreq)*self.fft_len/self.samp_rate
+ return index
def handle_ctrl_msg(self,msg):
m = pmt.pmt_to_python.pmt_to_dict(msg)
- #print(m)
+ if m.has_key("cmd") and m["cmd"]=="set_audio_freq":
+ #print(m)
+ #print(self.last_station_indices)
+ freq_index=self.freq_to_index(m["freq"])
+ if m["chan"]=="left" and freq_index1),N,Q,T,D,U,C,R ,Comment
event_array=self.tableobj.ecl_dict[ecn]
@@ -65,13 +66,15 @@ class tmc_event:
self.text_raw=event_array[1]
self.name=self.text_noQ
else:
- self.text_raw=event_array[0]
+ 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.length_str=None
- self.speed_limit_str=None
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
self.direction=event_array[8]#D:direction: 1:unidirectional, 2:bidirectional
@@ -112,12 +115,15 @@ class tmc_event:
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 #TODO translate
- quantifier_string="%i Metern"
+ 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="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
@@ -130,34 +136,48 @@ class tmc_event:
weight=10+0.5*(Q-100)
quantifier_string="%it"%weight
self.name=self.text_pluralQ.replace("(Q)",quantifier_string)
- #print(quantifier_string)
elif self.quantifierType=="9":
if Q<=100:
length=Q*0.1
else:
length=10+0.5*(Q-100)
quantifier_string="%.1fm"%length
- #print(quantifier_string)
self.name=self.text_pluralQ.replace("(Q)",quantifier_string)
- #print(self.name)
- self.name=self.text_raw+"; Q="+quantifier_string
- #if not self.length_str == None:
- # self.name=self.name.replace("(L)",self.length_str)
- def __repr__(self):
- return self.text_raw
+ else:#other quantifier
+ self.name=self.text_raw+"; Q(%s)=%s"%(self.quantifierType,quantifier_string)
def __str__(self):
- 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
- def __add_tag(self,tag):
- pass
+ 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 ""
@@ -172,6 +192,20 @@ class tmc_location:
#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)
@@ -196,6 +230,8 @@ class tmc_location:
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]
@@ -208,6 +244,10 @@ class tmc_location:
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
@@ -225,7 +265,120 @@ class tmc_location:
#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 items"
+ marker_template="addMarker({{lat: {lat}, lng: {lon}}},'{text}')"
+ def __init__(self):
+ self.messages=dict()
+ def add(self,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 getMarkerString(self):
+ markerstring=""
+ for lcn in self.messages:
+
+
+
+ loc=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:
+ loc=message.location
+ #map_tag+=str(updateClass)+": "
+ map_tag+='
'%color
+ map_tag+=message.map_string()
+ map_tag+="
"
+ map_tag+="
"
+ map_tag+="
"
+ if not loc==None:
+ markerstring+=tmc_dict.marker_template.format(lat=loc.ykoord,lon=loc.xkoord,text=map_tag)
+ 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
@@ -235,20 +388,98 @@ class tmc_message:
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))
+ #multi+=";events:"
+ #for i,event in enumerate(self.events):
+ #if not i==0:
+ #multi+=str(event)+","
+ except AttributeError:
+ multi="[multi incomplete]"
+ if not self.cancellation_time==None:
+ if language=="de":
+ multi+=" (aufgehoben um %s)"%self.cancellation_time
+ else:
+ multi+=" (cancelled at %s)"%self.cancellation_time
+ multi+="; "+self.getDuration()
+ return str(multi)
+ 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.events_string()+"; "+str(self.location)+"; "+self.psn
+ #code above gives unicode-decode error with character Ä (probably in location)
+ #return "%s: %s: %s; %s; %s"%(str(self.event.updateClass),self.getTime(),self.events_string(),str(self.location),self.psn)
+ #return str(self.event.updateClass)+": "+self.getTime()+": "+self.events_string()+"; "+str(self.location)+"; "+self.psn
+ return str(self.event.updateClass)+": "+self.getTime()+": "+self.display_text()+"; "+self.psn
+ def db_string(self):
+ return str(self.location)+": "+str(self.event.updateClass)+": "+self.display_text()
+ def map_string(self):
+ return str(self.event.updateClass)+": "+self.getTime()+": "+self.display_text()+"; "+self.multi_str()+"; "+self.psn
+ #code above gives unicode-decode error with character Ä (probably in location)
+ #return "%s: %s: %s; %s; %s"%(str(self.event.updateClass),self.getTime(),self.events_string(),self.multi_str(),self.psn)
+ #return str(self.event.updateClass)+": "+self.getTime()+": "+self.events_string()+"; "+self.multi_str()+"; "+self.psn
+ def display_text(self):
+ text=self.events_string()+"; "+str(self.location)#use events_string if no display_text implemented
+ #TODO add "dreieck" for P1.2 -> done in tmc_message.__str__
+ if not self.location.linRef==None:#test
+ self.tmc_extent
+ self.tmc_dir
+ offset_loc=self.location.get_extent_location(self.location,self.tmc_extent,self.tmc_dir)
+ if offset_loc.is_valid:
+ offset_loc_name=str(offset_loc)
+ else:
+ print(offset_loc)
+ offset_loc_name="###INVALID###"
+ templates={"de":"{A}, {B} in Richtung {C}, zwischen {D} und {E}, {F}"#codeing handbook: zwischen {D} und {E}, sprachdurchsagen: zwischen {E} und {D}
+ ,"en":"{A}, {B} {C}, {F}, between {D} and {E}"}
+ text=templates[language].format(A=self.location.linRef.roadnumber, B=self.location.linRef.second_name,C=self.location.linRef.first_name,D=str(self.location),E=offset_loc_name,F=self.events_string())
+
+ #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 __init__(self,PI,tmc_x,tmc_y,tmc_z,tableobj):#TODO handle out of sequence data
+ 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.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.isCancelled=False
+ self.psn=tableobj.RDS_data[PI]["PSN"]
+ 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)
- self.PI=PI
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)
@@ -276,8 +507,12 @@ class tmc_message:
#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
@@ -310,6 +545,11 @@ class tmc_message:
self.tmc_DP=data.uint
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
@@ -320,6 +560,8 @@ class tmc_message:
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)
@@ -433,16 +675,17 @@ 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):
+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")
- #self.db.commit()
- self.db.close()
- def __init__(self,signals,nPorts,slot,freq,log,debug,workdir):
+ 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",
@@ -460,6 +703,7 @@ class rds_parser_table_qt(gr.sync_block):
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
@@ -469,26 +713,33 @@ class rds_parser_table_qt(gr.sync_block):
self.TMC_data={}
self.IH_data={}
self.decoder_frequencies={}
- self.colorder=['ID','freq','name','PTY','AF','time','text','quality','buttons']
+ #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()
- #create new DB file
- self.db_name=workdir+'RDS_data'+datetime.now().strftime("%Y%m%d_%H%M%S")+'.db'
- db=sqlite3.connect(self.db_name, check_same_thread=False)
-
- #create tables
- 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()
+ 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")
"""
tmc_F=(tmc_x>>3)&0x1 #single/multiple group
tmc_event=int(tmc_y&0x7ff) #Y10-Y0
@@ -497,9 +748,6 @@ class rds_parser_table_qt(gr.sync_block):
tmc_extent=(tmc_y>>11)&0x7 #3 bits (Y13-Y11)
tmc_D=tmc_y>>15 #diversion bit(Y15)
tmc_dir=(tmc_y>>14)&0x1 #+-direction bit (Y14)"""
-
- self.db=db#TODO fix sqlite
-
#self.dbc.execute('''CREATE TABLE rtp
# (time text,PI text,rtp_string text)''')
@@ -558,14 +806,17 @@ class rds_parser_table_qt(gr.sync_block):
#self.osm_map = folium.Map(location=[48.7,9.2],zoom_start=10)#centered on stuttgart
#self.osm_map.save(self.workdir+'osm.html')
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)
- self.db.commit()
+ if self.writeDB:
+ self.db.commit()
#self.osm_map.save(self.workdir+'osm.html')
f=open(self.workdir+'google_maps_markers.js', 'w')
- markerstring="\n".join(self.map_markers)
+ #markerstring="\n".join(self.map_markers)
+ 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)
@@ -610,14 +861,16 @@ class rds_parser_table_qt(gr.sync_block):
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":""}
+ self.RDS_data[PI]["internals"]={"last_rt_tooltip":"","unfinished_TMC":{},"last_valid_rt":"","last_valid_psn":""}
+ 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 time.time()-self.save_data_timer > 10:#every 10 seconds
self.save_data_timer=time.time()
self.clean_data_and_commit_db()
#pr.enable()#disabled-internal-profiling
- #db=sqlite3.connect(self.db_name)
- db=self.db
+ if self.writeDB:
+ #db=sqlite3.connect(self.db_name)
+ db=self.db
array=pmt.to_python(msg)[1]
groupNR=array[2]&0b11110000
@@ -626,6 +879,10 @@ class rds_parser_table_qt(gr.sync_block):
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
@@ -661,10 +918,12 @@ class rds_parser_table_qt(gr.sync_block):
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
- db.execute("INSERT OR REPLACE INTO grouptypeCounts (PI,grouptype,count) VALUES (?,?,?)",t)
+ 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})
#save block to sqlite (commit at end of handle_msg)
#(time text,PI text,PSN text, grouptype text,content blob)
@@ -682,7 +941,7 @@ class rds_parser_table_qt(gr.sync_block):
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
+ 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
@@ -690,8 +949,19 @@ class rds_parser_table_qt(gr.sync_block):
TA=(array[3]>>4)&0x1
MS=(array[3]>>3)&0x1
self.RDS_data[PI]["TA"]=TA
- flag_string="TP:%i, TA:%i, MS:%i, DI:%s"%(TP,TA,MS,str(self.RDS_data[PI]["DI"]))
- self.signals.DataUpdateEvent.emit({'row':port,'PI':PI,'flags':flag_string})
+ #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
@@ -706,7 +976,8 @@ class rds_parser_table_qt(gr.sync_block):
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"]))
- db.execute("INSERT INTO stations (PI,PSN,freq,PTY,TP) VALUES (?,?,?,?,?)",t)
+ 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
@@ -715,7 +986,8 @@ class rds_parser_table_qt(gr.sync_block):
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"]))
- db.execute("INSERT INTO stations (PI,PSN,freq,PTY,TP) VALUES (?,?,?,?,?)",t)
+ 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"])
@@ -768,9 +1040,11 @@ class rds_parser_table_qt(gr.sync_block):
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"])
- db.execute("INSERT INTO data (time,PI,PSN,dataType,data) VALUES (?,?,?,?,?)",t)
+ if self.writeDB:
+ db.execute("INSERT INTO data (time,PI,PSN,dataType,data) VALUES (?,?,?,?,?)",t)
t=(self.RDS_data[PI]["PSN"],PI)
- db.execute("UPDATE OR IGNORE stations SET PSN=? WHERE PI IS ?",t)
+ 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"
@@ -796,7 +1070,8 @@ class rds_parser_table_qt(gr.sync_block):
#%02X%02X%02X%02X%02X
t=(str(datetime.now()),PI,self.RDS_data[PI]["PSN"],"PIN",data_string)
- db.execute("INSERT INTO data (time,PI,PSN,dataType,data) VALUES (?,?,?,?,?)",t)
+ 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:
@@ -865,11 +1140,13 @@ class rds_parser_table_qt(gr.sync_block):
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
t=(str(datetime.now()),PI,self.RDS_data[PI]["PSN"],"RT",rt)
- db.execute("INSERT INTO data (time,PI,PSN,dataType,data) VALUES (?,?,?,?,?)",t)
+ 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:#print rt+ if it exist
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)
+ if self.writeDB:
+ db.execute("INSERT INTO data (time,PI,PSN,dataType,data) VALUES (?,?,?,?,?)",t)
except KeyError:
pass#no rt+ -> dont save
else:
@@ -925,29 +1202,44 @@ class rds_parser_table_qt(gr.sync_block):
elif self.debug:
print("unknown variant %i in TMC 3A group"%variant)
elif (groupType == "4A"):#CT clock time
- datecode=((array[3] & 0x03) << 15) | (array[4] <<7)|((array[5] >> 1) & 0x7f)
- hours=((array[5] & 0x1) << 4) | ((array[6] >> 4) & 0x0f)
- minutes=((array[6] &0x0F)<<2)|((array[7] >>6)&0x3)
- offsetdir=(array[7]>>5)&0x1
- local_time_offset=0.5*((array[7])&0x1F)
- if(offsetdir==1):
- local_time_offset*=-1
- year=int((datecode - 15078.2) / 365.25)
- month=int((datecode - 14956.1 - int(year * 365.25)) / 30.6001)
- day=datecode - 14956 - int(year * 365.25) - int(month * 30.6001)
- if(month == 14 or month == 15):#no idea why -> annex g of RDS spec
- year += 1;
- month -= 13
- year+=1900
- #month was off by one different rounding in c and python?
- month-=1
- #datestring="%02i.%02i.%4i, %02i:%02i (%+.1fh)" % (day,month,year,hours,minutes,local_time_offset)
- timestring="%02i:%02i (%+.1fh)" % (hours,minutes,local_time_offset)
- datestring="%02i.%02i.%4i" % (day,month,year)
- ctcol=self.colorder.index('time')
- self.signals.DataUpdateEvent.emit({'col':ctcol,'row':port,'PI':PI,'string':timestring,'tooltip':datestring})
- t=(str(datetime.now()),PI,self.RDS_data[PI]["PSN"],"CT",datestring+" "+timestring)
- db.execute("INSERT INTO data (time,PI,PSN,dataType,data) VALUES (?,?,?,?,?)",t)
+ 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
+ #year=int((datecode - 15078.2) / 365.25)
+ #month=int((datecode - 14956.1 - int(year * 365.25)) / 30.6001)
+ #day=datecode - 14956 - int(year * 365.25) - int(month * 30.6001)
+ #if(month == 14 or month == 15):#no idea why -> annex g of RDS spec
+ #year += 1;
+ #month -= 13
+ #year+=1900
+ #month was off by one different rounding in c and python?
+ #month-=1# 13.1.2017, month now not off by one
+ #maybe the use of unsigned ints?
+ date=datetime(1858,11,17)+timedelta(days=int(datecode))#convert from MJD (modified julian date)
+
+ #datestring="%02i.%02i.%4i, %02i:%02i (%+.1fh)" % (day,month,year,hours,minutes,local_time_offset)
+ timestring="%02i:%02i (%+.1fh)" % (hours,minutes,local_time_offset)
+ datestring="%02i.%02i.%4i" % (date.day,date.month,date.year)
+ 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,
@@ -975,13 +1267,15 @@ class rds_parser_table_qt(gr.sync_block):
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,self)
+ 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,self)
+ 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()}
@@ -1045,7 +1339,8 @@ class rds_parser_table_qt(gr.sync_block):
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+"]))
- db.execute("INSERT INTO data (time,PI,PSN,dataType,data) VALUES (?,?,?,?,?)",t)
+ 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')
@@ -1070,8 +1365,8 @@ class rds_parser_table_qt(gr.sync_block):
tags="ir:%i,it:%i"%(item_running_bit,item_toggle_bit)
- afcol=self.colorder.index('AF')
- self.signals.DataUpdateEvent.emit({'col':afcol,'row':port,'PI':PI,'string':tags})
+ 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")):#TODO remove duplicate code
rt=self.RDS_data[PI]["RT"]
rt_valid=self.RDS_data[PI]["RT_valid"]
@@ -1145,7 +1440,8 @@ class rds_parser_table_qt(gr.sync_block):
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"]))
- db.execute("INSERT OR REPLACE INTO stations (PI,PSN,freq,PTY,TP) VALUES (?,?,?,?,?)",t)
+ 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
@@ -1215,34 +1511,18 @@ class rds_parser_table_qt(gr.sync_block):
reflocs=tmc_msg.location.reflocs
if not self.TMC_data.has_key(tmc_hash):#if message new
try:
- if tmc_msg.is_single:
- #multi_str="single"
- multi_str=""
- else:
- #multi_str="length:%i, list:%s"%(tmc_msg.length,str(tmc_msg.mgm_list))
- multi_str="%i:%s"%(tmc_msg.length,str(tmc_msg.mgm_list))
- 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-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':multi_str})
+ 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)
-
- message_string="%s ,locname:%s, reflocs:%s"%(str(tmc_msg.event),tmc_msg.location,reflocs)
- t=(tmc_hash,str(datetime.now()),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"),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)
- #self.signals.DataUpdateEvent.emit({'TMC_log_str':multi_str})
- if tmc_msg.location.has_koord:#show on map
- map_tag=str(tmc_msg.event)+"; "+multi_str
- #print to osm map (disabled because slow)
- #folium.Marker([tmc_msg.location.ykoord,tmc_msg.location.xkoord], popup=map_tag.decode("utf-8")).add_to(self.osm_map)
-
- #print to google map
- marker_string=self.marker_template.format(lat=tmc_msg.location.ykoord,lon=tmc_msg.location.xkoord,text=map_tag,marker_id=len(self.map_markers))
- marker_string=self.marker_template.format(lat=tmc_msg.location.ykoord,lon=tmc_msg.location.xkoord,text=map_tag)#without ID
- self.map_markers.append(marker_string)
-
-
-
+ 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
@@ -1326,10 +1606,11 @@ class rds_parser_table_qt_Widget(QtGui.QWidget):
self.table=QtGui.QTableWidget(self)
rowcount=0
self.table.setRowCount(rowcount)
- self.table.setColumnCount(9)
+ self.table.setColumnCount(10)
self.table.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers) #disallow editing
- self.colorder=['ID','freq','name','PTY','AF','time','text','quality','buttons']
+ #self.colorder=['ID','freq','name','buttons','PTY','AF','time','text','quality']
+ self.colorder=tableobj.colorder
##button.clicked.connect(self.getDetails)
layout.addWidget(self.table)
@@ -1395,11 +1676,27 @@ class rds_parser_table_qt_Widget(QtGui.QWidget):
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
+ #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=QtGui.QPushButton("getDetails")
- self.table.setCellWidget(rowPosition,self.table.columnCount()-1,button)
- button.clicked.connect(functools.partial(self.getDetails, row=rowPosition))
+ 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('decoder_frequencies'):
@@ -1412,7 +1709,8 @@ class rds_parser_table_qt_Widget(QtGui.QWidget):
reflocs_cmp=unicode(reflocs, encoding="UTF-8").lower()
event_cmp=unicode(str(tmc_msg.event), encoding="UTF-8").lower()
if not reflocs_cmp.find(lf)==-1 and not event_cmp.find(ef)==-1:
- 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.tableobj.RDS_data[tmc_msg.PI]["PSN"])
+ message_string=tmc_msg.log_string()
+ #message_string="TMC-message,event:%s lcn:%i,location:%s station:%s"%(str(tmc_msg.event),tmc_msg.location.lcn,str(tmc_msg.location),self.tableobj.RDS_data[tmc_msg.PI]["PSN"])
self.logOutput.append(Qt.QString.fromUtf8(message_string))
if event.has_key('multi_str'):
self.logOutput.append(Qt.QString.fromUtf8(event['multi_str']))
@@ -1446,7 +1744,21 @@ class rds_parser_table_qt_Widget(QtGui.QWidget):
item.setText(event['PTY'])
if event.has_key('flags'):
item=self.table.cellWidget(row,self.colorder.index('PTY'))
+ #TODO set color if TA changed
item.setToolTip(Qt.QString(event['flags']))
+ #TP=self.tableobj.RDS_data[PI]["TP"]
+ #TA=self.tableobj.RDS_data[PI]["TA"]
+ #if TP==1:
+ #s=str(item.text())
+ #if TA==1:
+ #color="red"
+ #elif TA==0:
+ #color="green"
+ #else:
+ #color="yellow"
+ ##s=re.sub("style='.*'","style='color:green'",s)
+ #s_colored="%s"%(color,s)
+ #item.setText(s_colored)
if event.has_key('string'):
item=self.table.cellWidget(row,event['col'])
item.setText(event['string'])
@@ -1496,8 +1808,20 @@ class rds_parser_table_qt_Widget(QtGui.QWidget):
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})
+ #send_pmt = pmt.pmt_to_python.pmt_from_dict({"cmd":"set_audio_freq","chan":"left","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())
@@ -1528,8 +1852,13 @@ class rds_parser_table_qt_Widget(QtGui.QWidget):
l.setTextInteractionFlags(QtCore.Qt.TextSelectableByMouse |QtCore.Qt.TextSelectableByKeyboard)
l.setWordWrap(True)
#l=QtGui.QLabel("Data:")
- view.layout().addWidget(l)
+ #view.layout().addWidget(l)
+
+ scrollArea = QtGui.QScrollArea(self)
+ scrollArea.setWidgetResizable(True)
+ scrollArea.setWidget(l)
+ view.layout().addWidget(scrollArea)
view.exec_()
def onCLick(self):
print("button clicked")
@@ -1550,3 +1879,4 @@ if __name__ == "__main__":
widget = None
+
diff --git a/python/rds_parser_table_qt.pyc b/python/rds_parser_table_qt.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..c330c4ff25ab51e969785232402723e31b6e4552
GIT binary patch
literal 38980
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%0DX|>lYG+qv6GRt%=L4|%cJ3!tDTBhoXJRZ<2?`kc
zFV*;Qfr>O@m{M5)N#nt8?^*87iSa8DJChkB6DpUHrd+5Au?jPwNhl^gA&eN6Sb$}U
zR@<0sD)-wJszt-sEqg@+FY4n%Jmt{_IaG)b{<#LsMr~Ss6Gw?S;ibrUg6Mfs_x$WT
zsP-ux@yWq_kvnrT2ArF|BC_GpLkuPhQuuK3t#YQ4i}lIt?5{~z^tTH>rn)~a=My-d
zPw~l{bb|~m?BB&m5Uds0S8$$egg+1D&uN5ZBG8WQPMAeIhwor8{*x!-k2MzYj{w(9
z=ZE#wWuX_y&@zg{~iG>g}W?2&fP3~RX8sVVLO(TO~
zC57MVPn-X<9PU?I1NU2Rd;M9MpPeUzeDrEKG~a1Q+e{)9=ks5m9P2V^W3z+`2^4s?
zMl%H^ZJQlh)3l*&DdA5Ra=#a#sGap`A$HMkc163?8+?PXSzOrc9pHjyp3@{>CeE(s
zn(m*40{2bA3>oX5w*DWo+?a?DR;?pnahyIt=#?|NNN@F-r59G{DSw!f(PXBj2fF
zX8cwz^TKb7S_%6Pz@1Dzm{)i20-`}?Fdi(yArJ|BSw*bXcAw4u;itU}ZAdIO(PveJ
zgddoZv#$&Lk54Lu)yazz3;1{N#}sc&90;^qR;lf_skeuZXM5F#g8$KM|Y>EPp1Sb=yYKj5qr!z}AuR
zY3qIPF9jsN@nkGR_Pk%!ARMD0hy1Yvw!vv|u?(2U(&QGFggq<}tDCx_ZTaP-G$X-?
zwXE|;sVKxw0GZ^mSxqwffp&};!~#eo_m*_!jYHfRG%KV34%86we#ErF>?uuz{bb$w
z9C`jo-4Vw1sbK%r`R7#VNjdxEmG^?B0_0;r+*aym{sO75sC-s~&__EnfOYsVDP~
zDESRJ897>1t+H%oB-87O>CY+Ai^$X0s>eJ93$uPb{R{bq1fl^NsBkJ|dH^{YGpm^Id.
+#
+# 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,time,functools
+class RingBuffer():
+ "A 1D ring buffer using numpy arrays"
+ def __init__(self, length):
+ self.data = np.zeros(length, dtype='f')
+ self.index = 0
+ def extend(self, x):
+ "adds array x to ring buffer"
+ x_index = (self.index + np.arange(x.size)) % self.data.size
+ self.data[x_index] = x
+ self.index = x_index[-1] + 1
+ def get(self):
+ "Returns the first-in-first-out data in the ring buffer"
+ idx = (self.index + np.arange(self.data.size)) %self.data.size
+ return self.data[idx]
+class stream_selector(gr.sync_block):
+ """
+ docstring for block stream_selector
+ """
+ def __init__(self):
+ gr.sync_block.__init__(self,
+ name="stream_selector",
+ in_sig=[np.float32,np.float32],
+ out_sig=None
+ )
+ #self.repeat_time=repeat_time
+ self.repeat_time=1
+ moving_avg_len=50
+ self.message_port_register_out(pmt.intern('rds_out'))
+ self.message_port_register_in(pmt.intern('rds_arg'))
+ self.set_msg_handler(pmt.intern('rds_arg'), functools.partial(self.handle_msg, port="arg"))
+ self.message_port_register_in(pmt.intern('rds_re'))
+ self.set_msg_handler(pmt.intern('rds_re'), functools.partial(self.handle_msg, port="re"))
+
+ #self.port_of_recent_messages=RingBuffer(10)#change to more fitting ringbuffer for ints
+ self.port_of_recent_messages=0#re as default
+ self.real_abs=RingBuffer(moving_avg_len)
+ self.arg_diff_abs=RingBuffer(moving_avg_len)
+ self.msg_time=time.time()
+ def handle_msg(self,msg,port):
+ msg_py=pmt.to_python(msg)
+ if port=="re":
+ #self.port_of_recent_messages.extend(np.array([0],dtype='f'))
+ self.port_of_recent_messages=(self.port_of_recent_messages*19+0)/20
+ if port=="arg":
+ self.port_of_recent_messages=(self.port_of_recent_messages*19+100)/20
+ #self.port_of_recent_messages.extend(np.array([100],dtype='f'))
+ #print(np.average(self.port_of_recent_messages.get()))
+ print(self.port_of_recent_messages)
+ print("msg: %s, port:%s"%(msg_py,port))
+
+ def work(self, input_items, output_items):
+ self.real_abs.extend(np.absolute(input_items[0]))
+ self.arg_diff_abs.extend(np.absolute(input_items[1]))
+
+ if(time.time()-self.msg_time > self.repeat_time):
+ self.msg_time=time.time()
+ #print("real: ")
+ ##print(real_abs)
+ #print(np.average(self.real_abs.get()))
+ #print("arg: ")
+ ##print(arg_diff_abs)
+ #print(np.average(self.arg_diff_abs.get()))
+ return len(input_items[0])