Browse Source

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

master
Clemens Richter 9 years ago
parent
commit
ebf406f84f
  1. 3
      grc/CMakeLists.txt
  2. 18
      grc/crfa_rds_parser_table_qt.xml
  3. 38
      grc/crfa_stream_selector.xml
  4. 4
      lib/rds_decoder_impl.cc
  5. 3
      python/CMakeLists.txt
  6. 12
      python/__init__.py
  7. 45
      python/max_freq.py
  8. 598
      python/rds_parser_table_qt.py
  9. BIN
      python/rds_parser_table_qt.pyc
  10. 87
      python/stream_selector.py

3
grc/CMakeLists.txt

@ -23,5 +23,6 @@ install(FILES
crfa_rds_parser_table_qt.xml crfa_rds_parser_table_qt.xml
crfa_rds_decoder.xml crfa_rds_decoder.xml
crfa_max_freq.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
) )

18
grc/crfa_rds_parser_table_qt.xml

@ -12,7 +12,7 @@
#set $label = '"%s"'%$id #set $label = '"%s"'%$id
#end if #end if
$(signals) = rds_parser_table_qt_Signals() $(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)) $(win) = rds_parser_table_qt_Widget($signals, $label,self.$(id))
$(gui_hint()($win))</make> $(gui_hint()($win))</make>
<callback>set_freq_tune($freq_tune);</callback> <callback>set_freq_tune($freq_tune);</callback>
@ -78,7 +78,21 @@ $(gui_hint()($win))</make>
<key>False</key> <key>False</key>
</option> </option>
</param> </param>
<param>
<name>write Database</name>
<key>writeDB</key>
<value>False</value>
<type>bool</type>
<!--<hide>part</hide>-->
<option>
<name>Enable</name>
<key>True</key>
</option>
<option>
<name>Disable</name>
<key>False</key>
</option>
</param>
<!-- <!--
check if pty list file exists check if pty list file exists

38
grc/crfa_stream_selector.xml

@ -0,0 +1,38 @@
<?xml version="1.0"?>
<block>
<name>stream_selector</name>
<key>crfa_stream_selector</key>
<category>[crfa]</category>
<import>import crfa</import>
<make>crfa.stream_selector()</make>
<!-- <param>
<name>...</name>
<key>...</key>
<type>...</type>
</param>-->
<sink>
<name>in_re</name>
<type>float</type>
</sink>
<sink>
<name>in_arg</name>
<type>float</type>
</sink>
<sink>
<name>rds_re</name>
<type>message</type>
<optional>1</optional>
</sink>
<sink>
<name>rds_arg</name>
<type>message</type>
<optional>1</optional>
</sink>
<source>
<name>rds_out</name>
<type>message</type>
<optional>1</optional>
</source>
</block>

4
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++; if (block_bit_counter<25) block_bit_counter++;
else { else {
good_block=false; 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); 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 */ /* manage special case of C or C' offset word */
if (block_number==2) { if (block_number==2) {
block_received_crc=checkword^offset_word[block_number]; block_received_crc=checkword^offset_word[block_number];

3
python/CMakeLists.txt

@ -37,7 +37,8 @@ GR_PYTHON_INSTALL(
rds_parser_table_qt.py rds_parser_table_qt.py
max_freq.py max_freq.py
smooth_vectors.py smooth_vectors.py
chart.py DESTINATION ${GR_PYTHON_DIR}/crfa chart.py
stream_selector.py DESTINATION ${GR_PYTHON_DIR}/crfa
) )
######################################################################## ########################################################################

12
python/__init__.py

@ -24,13 +24,12 @@ description here (python/__init__.py).
''' '''
# import swig generated symbols into the crfa namespace # import swig generated symbols into the crfa namespace
#try: try:
## this might fail if the module is python-only # this might fail if the module is python-only
#from crfa_swig import * from crfa_swig import *
#except ImportError: except ImportError:
#pass pass
from crfa.crfa_swig import *
# import any pure python here # import any pure python here
from multi_rds_printer import multi_rds_printer from multi_rds_printer import multi_rds_printer
from qtguitest import qtguitest 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 max_freq import max_freq
from smooth_vectors import smooth_vectors from smooth_vectors import smooth_vectors
from chart import Chart from chart import Chart
from stream_selector import stream_selector
# #

45
python/max_freq.py

@ -44,18 +44,46 @@ class max_freq(gr.sync_block):
self.message_port_register_in(pmt.intern('ctrl')) self.message_port_register_in(pmt.intern('ctrl'))
self.set_msg_handler(pmt.intern('ctrl'), self.handle_ctrl_msg) self.set_msg_handler(pmt.intern('ctrl'), self.handle_ctrl_msg)
self.searchMode=True 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): def handle_ctrl_msg(self,msg):
m = pmt.pmt_to_python.pmt_to_dict(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_index<self.fft_len-5:
if self.last_station_indices[0]==freq_index:
self.index_fixed[0]=False
print("decoder 0 free")
else:
self.last_station_indices[0]=freq_index
self.index_fixed[0]=True
print("decoder 0 fixed to %i"%m["freq"])
if m["chan"]=="right" and freq_index<self.fft_len-5:
if self.last_station_indices[1]==freq_index:
self.index_fixed[1]=False
print("decoder 1 free")
else:
self.last_station_indices[1]=freq_index
self.index_fixed[1]=True
print("decoder 1 fixed to %i"%m["freq"])
#print(self.last_station_indices)
if m.has_key("cmd") and m["cmd"]=="switch mode": if m.has_key("cmd") and m["cmd"]=="switch mode":
self.searchMode=not self.searchMode self.searchMode=not self.searchMode
print("searchMode: %s"%self.searchMode) print("searchMode: %s"%self.searchMode)
def set_center_freq(self, freq=None): def set_center_freq(self, freq=None):
if freq is not None: self.index_fixed=[False]*self.num_decoders#free all decoders (freq wouldn't match anyways)
if isinstance(freq, float) or isinstance(freq, int): if freq is not None:
self.center_freq=freq if isinstance(freq, float) or isinstance(freq, int):
else: self.center_freq=freq
self.center_freq = int(freq) else:
self.center_freq = int(freq)
def work(self, input_items, output_items): def work(self, input_items, output_items):
if self.counter<5: if self.counter<5:
self.counter+=1 self.counter+=1
@ -117,6 +145,11 @@ class max_freq(gr.sync_block):
station_indices_tune=[0]*self.num_decoders station_indices_tune=[0]*self.num_decoders
same_station_threshold=3 same_station_threshold=3
new_stations=[] new_stations=[]
#add fixed stations:
for i,old_freq in enumerate(self.last_station_indices):
if self.index_fixed[i]:
station_indices_tune[i]=old_freq
#change existing/add new:
for new_freq in station_indices_trunc: for new_freq in station_indices_trunc:
added=False added=False
for i,old_freq in enumerate(self.last_station_indices): for i,old_freq in enumerate(self.last_station_indices):

598
python/rds_parser_table_qt.py

@ -24,6 +24,7 @@ from gnuradio import gr
import pmt,functools,csv,md5,collections,copy,sqlite3,atexit,time,re,sys import pmt,functools,csv,md5,collections,copy,sqlite3,atexit,time,re,sys
#old imports: folium #old imports: folium
from datetime import datetime from datetime import datetime
from datetime import timedelta
import crfa.chart as chart import crfa.chart as chart
from PyQt4 import Qt, QtCore, QtGui from PyQt4 import Qt, QtCore, QtGui
@ -50,13 +51,13 @@ def ordinal(num):
suffix = SUFFIXES.get(num % 10, 'th') suffix = SUFFIXES.get(num % 10, 'th')
return str(num) + suffix return str(num) + suffix
class tmc_event: class tmc_event:
def __repr__(self):
return self.ecn
def __init__(self,ecn,tableobj): def __init__(self,ecn,tableobj):
self.tableobj=tableobj self.tableobj=tableobj
self.ecn=ecn self.ecn=ecn
self.text_raw="##Error##" self.text_raw="##Error##"
self.name="##Error##" self.name="##Error##"
self.length_str=None
self.speed_limit_str=None
try: 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 #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] event_array=self.tableobj.ecl_dict[ecn]
@ -65,13 +66,15 @@ class tmc_event:
self.text_raw=event_array[1] self.text_raw=event_array[1]
self.name=self.text_noQ self.name=self.text_noQ
else: 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.name=re.sub("\(([^()]+)\)","",self.text_raw)#removes everything between parentheses (assume no quantifier is used)
self.text_singleQ=event_array[3] self.text_singleQ=event_array[3]
self.text_pluralQ=event_array[4] 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 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.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.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 self.direction=event_array[8]#D:direction: 1:unidirectional, 2:bidirectional
@ -112,12 +115,15 @@ class tmc_event:
elif self.quantifierType=="1":#numbers 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] 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]) quantifier_string=str(numbers[Q-1])
elif self.quantifierType=="2":#z.b. für sichtweiten, e.g. for visibility #TODO translate elif self.quantifierType=="2":#z.b. für sichtweiten, e.g. for visibility
quantifier_string="%i Metern" quantifier_string="%i m"%(Q*10)#TODO meter oder metern?
self.name=self.text_pluralQ.replace("(Q)",quantifier_string) 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": elif self.quantifierType=="4":
speed=Q*5#in kmh 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": elif self.quantifierType=="7":
hours=int((Q-1)/6) hours=int((Q-1)/6)
minutes=((Q-1)%6)*10 minutes=((Q-1)%6)*10
@ -130,34 +136,48 @@ class tmc_event:
weight=10+0.5*(Q-100) weight=10+0.5*(Q-100)
quantifier_string="%it"%weight quantifier_string="%it"%weight
self.name=self.text_pluralQ.replace("(Q)",quantifier_string) self.name=self.text_pluralQ.replace("(Q)",quantifier_string)
#print(quantifier_string)
elif self.quantifierType=="9": elif self.quantifierType=="9":
if Q<=100: if Q<=100:
length=Q*0.1 length=Q*0.1
else: else:
length=10+0.5*(Q-100) length=10+0.5*(Q-100)
quantifier_string="%.1fm"%length quantifier_string="%.1fm"%length
#print(quantifier_string)
self.name=self.text_pluralQ.replace("(Q)",quantifier_string) self.name=self.text_pluralQ.replace("(Q)",quantifier_string)
#print(self.name) else:#other quantifier
self.name=self.text_raw+"; Q="+quantifier_string self.name=self.text_raw+"; Q(%s)=%s"%(self.quantifierType,quantifier_string)
#if not self.length_str == None:
# self.name=self.name.replace("(L)",self.length_str)
def __repr__(self):
return self.text_raw
def __str__(self): def __str__(self):
retstr=self.name if self.is_valid:
if not self.length_str == None: retstr=self.name
retstr=self.name.replace("(L)",self.length_str) if not self.length_str == None:
if not self.speed_limit_str == None: retstr=self.name.replace("(L)",self.length_str)
if language=="de": if not self.speed_limit_str == None:
retstr+=" (geschw. begrenzt: %s)"%self.speed_limit_str if language=="de":
else: retstr+=" (geschw. begrenzt: %s)"%self.speed_limit_str
retstr+=" (speed limit: %s)"%self.speed_limit_str else:
return retstr retstr+=" (speed limit: %s)"%self.speed_limit_str
def __add_tag(self,tag): return retstr
pass else:
return("invalid event, ecn:%i"%self.ecn)
def __repr__(self):
return "ecn:%i"%self.ecn
class tmc_location: 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=""): def __ref_locs(self,lcn,name_string=""):
#if not self.is_valid: #not used, since not called from outside #if not self.is_valid: #not used, since not called from outside
# return "" # return ""
@ -172,6 +192,20 @@ class tmc_location:
#return(loc_name) #return(loc_name)
except KeyError: except KeyError:
return(name_string) 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): def __repr__(self):
if not self.is_valid: if not self.is_valid:
return "invalid lcn:%i"%(self.lcn) return "invalid lcn:%i"%(self.lcn)
@ -196,6 +230,8 @@ class tmc_location:
self.reflocs=self.__ref_locs(lcn) self.reflocs=self.__ref_locs(lcn)
self.lcn=lcn self.lcn=lcn
self.has_koord=False self.has_koord=False
self.linRef=None
self.is_valid=False
try: try:
loc_array=tableobj.lcl_dict[lcn] loc_array=tableobj.lcl_dict[lcn]
self.ltype=loc_array[0] self.ltype=loc_array[0]
@ -208,6 +244,10 @@ class tmc_location:
self.roadname=loc_array[3] self.roadname=loc_array[3]
self.first_name=loc_array[4] self.first_name=loc_array[4]
self.second_name=loc_array[5] 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: try:
#koords stored in WGS84 format with decimal degrees multiplied with 10^5 #koords stored in WGS84 format with decimal degrees multiplied with 10^5
self.xkoord=int(loc_array[27])/100000.0 self.xkoord=int(loc_array[27])/100000.0
@ -225,7 +265,120 @@ class tmc_location:
#print("location '%i' not found"%lcn) #print("location '%i' not found"%lcn)
self.is_valid=False 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 ##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="<p>"
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+='<div style="color: %s;">'%color
map_tag+=message.map_string()
map_tag+="<br />"
map_tag+="</div>"
map_tag+="</p>"
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: 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 def __copy__(self):#doesn't copy, tmc_messages dont change if complete
return self return self
def __deepcopy__(self,memo):#return self, because deep copy fails def __deepcopy__(self,memo):#return self, because deep copy fails
@ -235,20 +388,98 @@ class tmc_message:
return self.tmc_hash return self.tmc_hash
else: else:
return self.ci 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): def __repr__(self):
#event_name=self.ecl_dict[self.tmc_event][1] #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"]) #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) 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
def __init__(self,PI,tmc_x,tmc_y,tmc_z,tableobj):#TODO handle out of sequence data 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.debug_data=""
self.tableobj=tableobj 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)) 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 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" assert tmc_T==0, "this is tuning info and no alert_c message"
Y15=int(tmc_y>>15) 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) 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_single=(tmc_F==1)
self.is_multi=(tmc_F==0) self.is_multi=(tmc_F==0)
@ -276,8 +507,12 @@ class tmc_message:
#self.event_name="##Error##" #self.event_name="##Error##"
self.tmc_extent=int((tmc_y>>11)&0x7) #3 bits (Y13-Y11) self.tmc_extent=int((tmc_y>>11)&0x7) #3 bits (Y13-Y11)
self.tmc_dir=int((tmc_y>>14)&0x1) #+-direction bit (Y14) 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 else:#subsequent groups in multigroup -> Y0..Y11 and Z0..Z15 are special format
raise ValueError, "subsequent groups must be added to existing tmc message" raise ValueError, "subsequent groups must be added to existing tmc message"
tableobj.tmc_messages.add(self)
def add_group(self,tmc_y,tmc_z): def add_group(self,tmc_y,tmc_z):
sg=int((tmc_y>>14)&0x1)#=1 if second group Y14 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 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 self.tmc_DP=data.uint
elif label==1 and data.uint==5: elif label==1 and data.uint==5:
self.tmc_D=1#set diversion bit 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 elif label==2:#length
last_event.add_length(data) last_event.add_length(data)
elif label==3:#speed elif label==3:#speed
@ -320,6 +560,8 @@ class tmc_message:
last_event.add_quantifier(data,8) last_event.add_quantifier(data,8)
elif label==9:#additional event elif label==9:#additional event
last_event=tmc_event(data.uint,self.tableobj) last_event=tmc_event(data.uint,self.tableobj)
if not last_event.is_valid:
print("invalid MGM event")
self.events.append(last_event) self.events.append(last_event)
@ -433,16 +675,17 @@ class rds_parser_table_qt_Signals(QObject):
DataUpdateEvent = QtCore.pyqtSignal(dict) DataUpdateEvent = QtCore.pyqtSignal(dict)
def __init__(self, parent=None): def __init__(self, parent=None):
super(QtCore.QObject, self).__init__() 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 docstring for block qtguitest
""" """
def goodbye(self): def goodbye(self):
self.clean_data_and_commit_db() self.clean_data_and_commit_db()
print("quitting rds parser table, closing db") print("quitting rds parser table, closing db")
#self.db.commit() if self.writeDB:
self.db.close() #self.db.commit()
def __init__(self,signals,nPorts,slot,freq,log,debug,workdir): self.db.close()
def __init__(self,signals,nPorts,slot,freq,log,debug,workdir,writeDB):
#QObject.__init__() #QObject.__init__()
gr.sync_block.__init__(self, gr.sync_block.__init__(self,
name="RDS Table", name="RDS Table",
@ -460,6 +703,7 @@ class rds_parser_table_qt(gr.sync_block):
self.message_port_register_out(pmt.intern('ctrl')) self.message_port_register_out(pmt.intern('ctrl'))
self.log=log self.log=log
self.debug=debug self.debug=debug
self.writeDB=writeDB
self.signals=signals self.signals=signals
self.RDS_data={} self.RDS_data={}
self.change_freq_tune=slot self.change_freq_tune=slot
@ -469,26 +713,33 @@ class rds_parser_table_qt(gr.sync_block):
self.TMC_data={} self.TMC_data={}
self.IH_data={} self.IH_data={}
self.decoder_frequencies={} 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.workdir=workdir
self.PI_dict={}#contains PI:numpackets (string:integer) 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' if self.writeDB:
db=sqlite3.connect(self.db_name, check_same_thread=False) #create new DB file
db_name=workdir+'RDS_data'+datetime.now().strftime("%Y%m%d_%H%M%S")+'.db'
#create tables db=sqlite3.connect(db_name, check_same_thread=False)
db.execute('''CREATE TABLE stations self.db=db
(PI text PRIMARY KEY UNIQUE,PSN text, freq real, PTY text,TP integer)''') #create tables
db.execute('''CREATE TABLE groups try:
(time text,PI text,PSN text, grouptype text,content blob)''') db.execute('''CREATE TABLE stations
db.execute('''CREATE TABLE data (PI text PRIMARY KEY UNIQUE,PSN text, freq real, PTY text,TP integer)''')
(time text,PI text,PSN text, dataType text,data blob)''') db.execute('''CREATE TABLE groups
db.execute('''CREATE TABLE grouptypeCounts (time text,PI text,PSN text, grouptype text,content blob)''')
(PI text,grouptype text,count integer,unique (PI, grouptype))''') db.execute('''CREATE TABLE data
db.execute('''CREATE TABLE TMC (time text,PI text,PSN text, dataType text,data blob)''')
(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.execute('''CREATE TABLE grouptypeCounts
db.commit() (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_F=(tmc_x>>3)&0x1 #single/multiple group
tmc_event=int(tmc_y&0x7ff) #Y10-Y0 tmc_event=int(tmc_y&0x7ff) #Y10-Y0
@ -498,9 +749,6 @@ class rds_parser_table_qt(gr.sync_block):
tmc_D=tmc_y>>15 #diversion bit(Y15) tmc_D=tmc_y>>15 #diversion bit(Y15)
tmc_dir=(tmc_y>>14)&0x1 #+-direction bit (Y14)""" tmc_dir=(tmc_y>>14)&0x1 #+-direction bit (Y14)"""
self.db=db#TODO fix sqlite
#self.dbc.execute('''CREATE TABLE rtp #self.dbc.execute('''CREATE TABLE rtp
# (time text,PI text,rtp_string text)''') # (time text,PI text,rtp_string text)''')
#workdir="/user/wire2/richter/hackrf_prototypes/" #workdir="/user/wire2/richter/hackrf_prototypes/"
@ -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 = folium.Map(location=[48.7,9.2],zoom_start=10)#centered on stuttgart
#self.osm_map.save(self.workdir+'osm.html') #self.osm_map.save(self.workdir+'osm.html')
atexit.register(self.goodbye) atexit.register(self.goodbye)
def clean_data_and_commit_db(self): def clean_data_and_commit_db(self):
for PI in self.PI_dict: for PI in self.PI_dict:
self.PI_dict[PI]-=1 self.PI_dict[PI]-=1
#print(self.PI_dict) #print(self.PI_dict)
self.db.commit() if self.writeDB:
self.db.commit()
#self.osm_map.save(self.workdir+'osm.html') #self.osm_map.save(self.workdir+'osm.html')
f=open(self.workdir+'google_maps_markers.js', 'w') 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 console.log("loaded "+markers.length+" markers")'
markerstring+='\n document.getElementById("errorid").innerHTML = "loaded "+markers.length+" markers";' markerstring+='\n document.getElementById("errorid").innerHTML = "loaded "+markers.length+" markers";'
f.write(markerstring) f.write(markerstring)
@ -611,13 +862,15 @@ class rds_parser_table_qt(gr.sync_block):
self.RDS_data[PI]["PTY"]="" self.RDS_data[PI]["PTY"]=""
self.RDS_data[PI]["DI"]=[2,2,2,2] 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 def handle_msg(self, msg, port):#port from 0 to 3
if time.time()-self.save_data_timer > 10:#every 10 seconds if time.time()-self.save_data_timer > 10:#every 10 seconds
self.save_data_timer=time.time() self.save_data_timer=time.time()
self.clean_data_and_commit_db() self.clean_data_and_commit_db()
#pr.enable()#disabled-internal-profiling #pr.enable()#disabled-internal-profiling
#db=sqlite3.connect(self.db_name) if self.writeDB:
db=self.db #db=sqlite3.connect(self.db_name)
db=self.db
array=pmt.to_python(msg)[1] array=pmt.to_python(msg)[1]
groupNR=array[2]&0b11110000 groupNR=array[2]&0b11110000
@ -626,6 +879,10 @@ class rds_parser_table_qt(gr.sync_block):
groupType=str(groupNR >> 4)+"A" groupType=str(groupNR >> 4)+"A"
else: else:
groupType=str(groupNR >> 4)+"B" 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]) PI="%02X%02X" %(array[0],array[1])
TP=(array[2]>>2)&0x1 TP=(array[2]>>2)&0x1
block2=(array[2]<<8)|(array[3]) #block2 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: if self.RDS_data[PI]["blockcounts"]["any"]==5:
self.RDS_data[PI]["blockcounts"]["any"]=0 self.RDS_data[PI]["blockcounts"]["any"]=0
t=(str(PI),groupType,self.RDS_data[PI]["blockcounts"][groupType])#TODO only update DB every few seconds 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"] dots="."*self.RDS_data[PI]["blockcounts"]["any"]
self.RDS_data[PI]["TP"]=TP self.RDS_data[PI]["TP"]=TP
self.RDS_data[PI]["PTY"]=self.pty_dict[PTY] 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}) 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) #save block to sqlite (commit at end of handle_msg)
#(time text,PI text,PSN text, grouptype text,content blob) #(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 adr=array[3]&0b00000011
segment=self.decode_chars(chr(array[6])+chr(array[7])) segment=self.decode_chars(chr(array[6])+chr(array[7]))
d=(array[3]>>2)&0x1 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 #DI[0]=d0 0=Mono 1=Stereo
#d1 Not artificial head Artificial head #d1 Not artificial head Artificial head
#d2 Not compressed Compressed #d2 Not compressed Compressed
@ -690,8 +949,19 @@ class rds_parser_table_qt(gr.sync_block):
TA=(array[3]>>4)&0x1 TA=(array[3]>>4)&0x1
MS=(array[3]>>3)&0x1 MS=(array[3]>>3)&0x1
self.RDS_data[PI]["TA"]=TA 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"])) #style='font-family:Courier New;color:%s'
self.signals.DataUpdateEvent.emit({'row':port,'PI':PI,'flags':flag_string}) flag_string="<span style=''>TP:%i, TA:%i, MS:%i, DI:%s</span>"%(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="<span style='color:%s'>%s</span>"%(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 #224 1110 0000 = no AF
#225 1110 0001 = 1AF #225 1110 0001 = 1AF
@ -706,7 +976,8 @@ class rds_parser_table_qt(gr.sync_block):
freq_str="0A:%0.1fM"% (freq/1e6) freq_str="0A:%0.1fM"% (freq/1e6)
self.signals.DataUpdateEvent.emit({'PI':PI,'freq':freq_str}) 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"])) 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]) freq=self.decode_AF_freq(array[5])
if freq==self.RDS_data[PI]["tuned_freq"]: if freq==self.RDS_data[PI]["tuned_freq"]:
self.RDS_data[PI]["AF"]["main"]=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) freq_str="0A:%0.1fM"% (freq/1e6)
self.signals.DataUpdateEvent.emit({'PI':PI,'freq':freq_str}) 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"])) 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 if self.RDS_data[PI].has_key("tuned_freq") :#TODO add secondary freqs
freq=self.decode_AF_freq(array[4]) freq=self.decode_AF_freq(array[4])
diff=abs(freq-self.RDS_data[PI]["tuned_freq"]) 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) 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 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"]) 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) 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"] self.RDS_data[PI]["internals"]["last_valid_psn"]=self.RDS_data[PI]["PSN"]
else: else:
textcolor="gray" textcolor="gray"
@ -796,7 +1070,8 @@ class rds_parser_table_qt(gr.sync_block):
#%02X%02X%02X%02X%02X #%02X%02X%02X%02X%02X
t=(str(datetime.now()),PI,self.RDS_data[PI]["PSN"],"PIN",data_string) 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 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)) print("PI:%s PSN:%s uses variant %i of 1A"%(PI,self.RDS_data[PI]["PSN"],variant))
if variant==0: 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 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 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) 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 self.RDS_data[PI]["internals"]["last_valid_rt"]=rt
try:#print rt+ if it exist try:#print rt+ if it exist
t=(str(datetime.now()),PI,self.RDS_data[PI]["PSN"],"RT+",str(self.RDS_data[PI]["RT+"])) 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: except KeyError:
pass#no rt+ -> dont save pass#no rt+ -> dont save
else: else:
@ -925,29 +1202,44 @@ class rds_parser_table_qt(gr.sync_block):
elif self.debug: elif self.debug:
print("unknown variant %i in TMC 3A group"%variant) print("unknown variant %i in TMC 3A group"%variant)
elif (groupType == "4A"):#CT clock time elif (groupType == "4A"):#CT clock time
datecode=((array[3] & 0x03) << 15) | (array[4] <<7)|((array[5] >> 1) & 0x7f) datecode=((array[3] & 0x03) << 15) | (array[4] <<7)|((array[5] >> 1) & 0x7f)#modified julian date
hours=((array[5] & 0x1) << 4) | ((array[6] >> 4) & 0x0f) if datecode==0:
minutes=((array[6] &0x0F)<<2)|((array[7] >>6)&0x3) #do not update!!
offsetdir=(array[7]>>5)&0x1 if self.debug:
local_time_offset=0.5*((array[7])&0x1F) print("station:%s sent empty 4A group"%self.RDS_data[PI]["PSN"])
if(offsetdir==1): else:
local_time_offset*=-1 hours=((array[5] & 0x1) << 4) | ((array[6] >> 4) & 0x0f)
year=int((datecode - 15078.2) / 365.25) minutes=((array[6] &0x0F)<<2)|((array[7] >>6)&0x3)
month=int((datecode - 14956.1 - int(year * 365.25)) / 30.6001) offsetdir=(array[7]>>5)&0x1
day=datecode - 14956 - int(year * 365.25) - int(month * 30.6001) local_time_offset=0.5*((array[7])&0x1F)
if(month == 14 or month == 15):#no idea why -> annex g of RDS spec if(offsetdir==1):
year += 1; local_time_offset*=-1
month -= 13 #year=int((datecode - 15078.2) / 365.25)
year+=1900 #month=int((datecode - 14956.1 - int(year * 365.25)) / 30.6001)
#month was off by one different rounding in c and python? #day=datecode - 14956 - int(year * 365.25) - int(month * 30.6001)
month-=1 #if(month == 14 or month == 15):#no idea why -> annex g of RDS spec
#datestring="%02i.%02i.%4i, %02i:%02i (%+.1fh)" % (day,month,year,hours,minutes,local_time_offset) #year += 1;
timestring="%02i:%02i (%+.1fh)" % (hours,minutes,local_time_offset) #month -= 13
datestring="%02i.%02i.%4i" % (day,month,year) #year+=1900
ctcol=self.colorder.index('time') #month was off by one different rounding in c and python?
self.signals.DataUpdateEvent.emit({'col':ctcol,'row':port,'PI':PI,'string':timestring,'tooltip':datestring}) #month-=1# 13.1.2017, month now not off by one
t=(str(datetime.now()),PI,self.RDS_data[PI]["PSN"],"CT",datestring+" "+timestring) #maybe the use of unsigned ints?
db.execute("INSERT INTO data (time,PI,PSN,dataType,data) VALUES (?,?,?,?,?)",t) 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 elif (groupType == "6A"):#IH inhouse data -> save for analysis
"""In House Data: """In House Data:
{'130A': {'1E1077FFFF': {'count': 1, {'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_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) 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) 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_T == 0:
if tmc_F==1:#single group 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) self.print_tmc_msg(tmc_msg)
elif tmc_F==0 and Y15==1:#1st group of multigroup elif tmc_F==0 and Y15==1:#1st group of multigroup
ci=int(tmc_x&0x7) 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): #if self.RDS_data[PI]["internals"]["unfinished_TMC"].has_key(ci):
#print("overwriting parital message") #print("overwriting parital message")
self.RDS_data[PI]["internals"]["unfinished_TMC"][ci]={"msg":tmc_msg,"time":time.time()} 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 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"] #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+"])) 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+_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 self.RDS_data[PI]["RT+"]["last_item_toggle_bit"] = item_toggle_bit
rtcol=self.colorder.index('text') 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) tags="ir:%i,it:%i"%(item_running_bit,item_toggle_bit)
afcol=self.colorder.index('AF') rtpcol=self.colorder.index('RT+')
self.signals.DataUpdateEvent.emit({'col':afcol,'row':port,'PI':PI,'string':tags}) 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 if(tag2_type=="ITEM.TITLE" and self.RDS_data[PI].has_key("RT")):#TODO remove duplicate code
rt=self.RDS_data[PI]["RT"] rt=self.RDS_data[PI]["RT"]
rt_valid=self.RDS_data[PI]["RT_valid"] 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}) self.signals.DataUpdateEvent.emit({'PI':PI_ON,'PSN':formatted_text})
try: 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"])) 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: except KeyError:
#not all info present -> no db update #not all info present -> no db update
pass pass
@ -1215,34 +1511,18 @@ class rds_parser_table_qt(gr.sync_block):
reflocs=tmc_msg.location.reflocs reflocs=tmc_msg.location.reflocs
if not self.TMC_data.has_key(tmc_hash):#if message new if not self.TMC_data.has_key(tmc_hash):#if message new
try: try:
if tmc_msg.is_single: #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"])
#multi_str="single" #message_string=tmc_msg.log_string()
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"])
self.TMC_data[tmc_hash]=tmc_msg 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")) #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) #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="%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)) message_string=tmc_msg.db_string()
self.db.execute("INSERT INTO TMC (hash,time,PI, F,event,location,DP,div,dir,extent,text,multi,rawmgm) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)",t) 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.signals.DataUpdateEvent.emit({'TMC_log_str':multi_str}) if self.writeDB:
if tmc_msg.location.has_koord:#show on map self.db.execute("INSERT INTO TMC (hash,time,PI, F,event,location,DP,div,dir,extent,text,multi,rawmgm) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)",t)
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)
except Exception as e: except Exception as e:
print(e) print(e)
raise raise
@ -1326,10 +1606,11 @@ class rds_parser_table_qt_Widget(QtGui.QWidget):
self.table=QtGui.QTableWidget(self) self.table=QtGui.QTableWidget(self)
rowcount=0 rowcount=0
self.table.setRowCount(rowcount) self.table.setRowCount(rowcount)
self.table.setColumnCount(9) self.table.setColumnCount(10)
self.table.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers) #disallow editing 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) ##button.clicked.connect(self.getDetails)
layout.addWidget(self.table) layout.addWidget(self.table)
@ -1395,11 +1676,27 @@ class rds_parser_table_qt_Widget(QtGui.QWidget):
def insert_empty_row(self): def insert_empty_row(self):
rowPosition = self.table.rowCount() rowPosition = self.table.rowCount()
self.table.insertRow(rowPosition) 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()) self.table.setCellWidget(rowPosition,col,QtGui.QLabel())
button=QtGui.QPushButton("getDetails") button_layout = Qt.QHBoxLayout()
self.table.setCellWidget(rowPosition,self.table.columnCount()-1,button) details_button=QtGui.QPushButton("Detail")
button.clicked.connect(functools.partial(self.getDetails, row=rowPosition)) 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): def display_data(self, event):
#pp.pprint(event) #pp.pprint(event)
if type(event)==dict and event.has_key('decoder_frequencies'): 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() reflocs_cmp=unicode(reflocs, encoding="UTF-8").lower()
event_cmp=unicode(str(tmc_msg.event), 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: 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)) self.logOutput.append(Qt.QString.fromUtf8(message_string))
if event.has_key('multi_str'): if event.has_key('multi_str'):
self.logOutput.append(Qt.QString.fromUtf8(event['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']) item.setText(event['PTY'])
if event.has_key('flags'): if event.has_key('flags'):
item=self.table.cellWidget(row,self.colorder.index('PTY')) item=self.table.cellWidget(row,self.colorder.index('PTY'))
#TODO set color if TA changed
item.setToolTip(Qt.QString(event['flags'])) 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="<span style='color:%s'>%s</span>"%(color,s)
#item.setText(s_colored)
if event.has_key('string'): if event.has_key('string'):
item=self.table.cellWidget(row,event['col']) item=self.table.cellWidget(row,event['col'])
item.setText(event['string']) item.setText(event['string'])
@ -1496,8 +1808,20 @@ class rds_parser_table_qt_Widget(QtGui.QWidget):
layout=Qt.QVBoxLayout() layout=Qt.QVBoxLayout()
layout.addWidget(l) layout.addWidget(l)
view.setLayout(layout) view.setLayout(layout)
view.exec_() 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): def getDetails(self,row):
PIcol=self.colorder.index('ID') PIcol=self.colorder.index('ID')
PI=str(self.table.cellWidget(row,PIcol).text()) 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.setTextInteractionFlags(QtCore.Qt.TextSelectableByMouse |QtCore.Qt.TextSelectableByKeyboard)
l.setWordWrap(True) l.setWordWrap(True)
#l=QtGui.QLabel("Data:") #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_() view.exec_()
def onCLick(self): def onCLick(self):
print("button clicked") print("button clicked")
@ -1550,3 +1879,4 @@ if __name__ == "__main__":
widget = None widget = None

BIN
python/rds_parser_table_qt.pyc

Binary file not shown.

87
python/stream_selector.py

@ -0,0 +1,87 @@
#!/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,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])
Loading…
Cancel
Save