diff --git a/grc/crfa_rds_parser_table_qt.xml b/grc/crfa_rds_parser_table_qt.xml index c27a4cf..56decb4 100644 --- a/grc/crfa_rds_parser_table_qt.xml +++ b/grc/crfa_rds_parser_table_qt.xml @@ -82,7 +82,7 @@ $(gui_hint()($win)) in message $nPorts - + freq diff --git a/python/max_freq.py b/python/max_freq.py index 3056484..130f74b 100644 --- a/python/max_freq.py +++ b/python/max_freq.py @@ -43,9 +43,13 @@ class max_freq(gr.sync_block): self.counter=0 self.message_port_register_in(pmt.intern('ctrl')) self.set_msg_handler(pmt.intern('ctrl'), self.handle_ctrl_msg) + self.searchMode=True def handle_ctrl_msg(self,msg): m = pmt.pmt_to_python.pmt_to_dict(msg) - print(m) + #print(m) + if m.has_key("cmd") and m["cmd"]=="switch mode": + self.searchMode=not self.searchMode + print("searchMode: %s"%self.searchMode) def set_center_freq(self, freq=None): if freq is not None: if isinstance(freq, float) or isinstance(freq, int): @@ -56,7 +60,7 @@ class max_freq(gr.sync_block): if self.counter<5: self.counter+=1 return len(input_items[0]) - else: + elif self.searchMode: self.counter=0 #in0 = input_items[0] #ii=input_items @@ -110,60 +114,6 @@ class max_freq(gr.sync_block): station_indices_trunc=list(station_indices_sorted)#copy list del station_indices_trunc[self.num_decoders:]#remove non decodable incidices - ############################### - #comparelist=[] - #for freq in station_indices_trunc: - #comparelist.append({"freq":freq,"decoder":None,"new":True})#new detected-> no assigned decoder - #for decnum,freq in enumerate(self.last_station_indices): - #comparelist.append({"freq":freq,"decoder":decnum,"new":False}) - ##comparelist.sort() - #comparelist_sorted=sorted(comparelist, key=lambda k: k['freq']) - #print(comparelist_sorted) - - #differences=[] - #station_indices_tune=[0]*4#TODO what if < 4 max freqs? - #last_elem=None - #same_station_threshold=2 - #new_freqs=[] - ##for elem in comparelist_sorted: - #while len(comparelist_sorted)>0: - #elem=comparelist_sorted.pop(0)#get and remove first - #freq=elem["freq"] - #if not last_elem==None and not elem["freq"]==0: - #fdiff=abs(freq-last_elem["freq"]) - #differences.append(fdiff) - #if fdiff use old decoder - #if elem["new"]:#if elem is new last_elem must be old - #decnum=last_elem["decoder"] - #freq=elem["freq"]#use new freq - #else: - #decnum=elem["decoder"] - #freq=last_elem["freq"]#use new freq - #station_indices_tune[decnum]=freq - #else:#stations different -> save last_elem and compare with next - #if last_elem["new"]:#save new - #new_freqs.append(last_elem["freq"]) - #last_elem=elem - #if last_elem["new"]:#save new - #new_freqs.append(last_elem["freq"]) - #station_indices_tune=list(set(station_indices_tune))#remove duplicates - #for i,freq in enumerate(station_indices_tune): - #if freq==0 and len(new_freqs)>0:#decoder unused - #station_indices_tune[i]=new_freqs.pop()#assign new freq - - #print("diff %s"%differences) - - #print("tune:%s"%station_indices_tune) - #print("nomatch:%s"%new_freqs) - ############################### - #problems: - #very slow, sometimes switches - - - ############################### - #station_indices_tune.sort() - ############################### - #problems: swtiching station_indices_tune=[0]*self.num_decoders same_station_threshold=3 new_stations=[] @@ -192,6 +142,7 @@ class max_freq(gr.sync_block): for index in station_indices_tune: startfreq=self.center_freq-self.samp_rate/2 freq=self.samp_rate*index/self.fft_len+startfreq + freq+=30000#add 30k because detected max often too low num_decimals=int(round(math.log(self.snapto,10))) station_freqs.append(round(freq,-num_decimals)) station_strength.append(round(numbers[index],-2)) @@ -207,4 +158,6 @@ class max_freq(gr.sync_block): print(station_freqs) return len(input_items[0]) + else: + return len(input_items[0]) diff --git a/python/rds_parser_table_qt.py b/python/rds_parser_table_qt.py index 1e45dc3..d8974dd 100644 --- a/python/rds_parser_table_qt.py +++ b/python/rds_parser_table_qt.py @@ -21,15 +21,15 @@ from __future__ import print_function#print without newline print('.', end="") import numpy from gnuradio import gr -import pmt,functools,csv,md5,collections,copy,sqlite3,atexit,time,folium +import pmt,functools,csv,md5,collections,copy,sqlite3,atexit,time,re,folium from datetime import datetime import crfa.chart as chart from PyQt4 import Qt, QtCore, QtGui import pprint,code,pickle#for easier testing pp = pprint.PrettyPrinter() -import cProfile, pstats, StringIO #for profiling -pr = cProfile.Profile() +#import cProfile, pstats, StringIO #for profiling +#pr = cProfile.Profile()#disabled-internal-profiling #from threading import Timer#to periodically save DB @@ -264,11 +264,11 @@ class tmc_message: if self.count==gsi: #group in sequence data1=int(tmc_y&0xfff)#data block 1 data2=int(tmc_z)#data block 2 - #code.interact(local=locals()) + self.data_arr.append("0x%03X"%data1)#3 hex characters self.data_arr.append("0x%04X"%data2)#4 hex characters #print(gsi) - #code.interact(local=locals()) + if self.count==0:#last group self.is_complete=True self.debug_data=copy.deepcopy(self.data_arr) @@ -567,7 +567,7 @@ class rds_parser_table_qt(gr.sync_block): self.RDS_data[PI]["AID_list"]={} self.RDS_data[PI]["PSN"]="_"*8 self.RDS_data[PI]["PSN_valid"]=[False]*8 - self.RDS_data[PI]["AF"]={} + self.RDS_data[PI]["AF"]={"set":set(),"rxset":set()} self.RDS_data[PI]["TP"]=-1 self.RDS_data[PI]["TA"]=-1 self.RDS_data[PI]["PTY"]="" @@ -577,8 +577,10 @@ class rds_parser_table_qt(gr.sync_block): 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() - #code.interact(local=locals()) + #pr.enable()#disabled-internal-profiling + #db=sqlite3.connect(self.db_name) + db=self.db + array=pmt.to_python(msg)[1] groupNR=array[2]&0b11110000 groupVar=array[2]&0b00001000 @@ -613,9 +615,15 @@ class rds_parser_table_qt(gr.sync_block): freq_str="%i:%0.1fM"% (port,freq/1e6) self.RDS_data[PI]["tuned_freq"]=freq #self.signals.DataUpdateEvent.emit({'PI':PI,'freq':freq_str}) + if self.RDS_data[PI]["blockcounts"].has_key(groupType): + self.RDS_data[PI]["blockcounts"][groupType] +=1 #increment + else: + self.RDS_data[PI]["blockcounts"][groupType] = 1 #initialize (1st group of this type) self.RDS_data[PI]["blockcounts"]["any"]+=1 if self.RDS_data[PI]["blockcounts"]["any"]==5: self.RDS_data[PI]["blockcounts"]["any"]=0 + t=(str(PI),groupType,self.RDS_data[PI]["blockcounts"][groupType])#TODO only update DB every few seconds + 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] @@ -623,22 +631,15 @@ class rds_parser_table_qt(gr.sync_block): #save block to sqlite (commit at end of handle_msg) #(time text,PI text,PSN text, grouptype text,content blob) content="%02X%02X%02X%02X%02X" %(array[3]&0x1f,array[4],array[5],array[6],array[7]) - #db=sqlite3.connect(self.db_name) - db=self.db + #t=(str(datetime.now()),PI,self.RDS_data[PI]["PSN"],groupType,content) #db.execute("INSERT INTO groups VALUES (?,?,?,?,?)",t) - if self.RDS_data[PI]["blockcounts"].has_key(groupType): - self.RDS_data[PI]["blockcounts"][groupType] +=1 #increment - else: - self.RDS_data[PI]["blockcounts"][groupType] = 1 #initialize (1st group of this type) + #error 161213: # db.execute("INSERT OR REPLACE INTO grouptypeCounts (PI,grouptype,count) VALUES (?,?,?)",t) #InterfaceError: Error binding parameter 0 - probably unsupported type. #fix?: added str() to PI, but it should already be a string - - 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 (groupType == "0A"):#AF PSN adr=array[3]&0b00000011 segment=self.decode_chars(chr(array[6])+chr(array[7])) @@ -677,13 +678,32 @@ class rds_parser_table_qt(gr.sync_block): 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.RDS_data[PI].has_key("tuned_freq") :#TODO add secondary freqs + freq=self.decode_AF_freq(array[4]) + diff=abs(freq-self.RDS_data[PI]["tuned_freq"]) + if diff<100000: + self.RDS_data[PI]["AF"]["rxset"].add(freq) + freq=self.decode_AF_freq(array[5]) + diff=abs(freq-self.RDS_data[PI]["tuned_freq"]) + if diff<100000: + self.RDS_data[PI]["AF"]["rxset"].add(freq) + if(array[4]>= 224 and array[4]<= 249): #print("AF1 detected") self.RDS_data[PI]["AF"]['number']=array[4]-224 #self.RDS_data[PI]["AF"]['main']=self.decode_AF_freq(array[5]) self.signals.DataUpdateEvent.emit({'row':port,'PI':PI,'AF':self.RDS_data[PI]["AF"]}) if(array[5]>= 224 and array[5]<= 249): - print("AF2 detected (shouldn't happen)") + print("AF2 detected (shouldn't happen) %s"%array[5]) + + + #add frequencies to set + self.RDS_data[PI]["AF"]["set"].add(self.decode_AF_freq(array[4])) + self.RDS_data[PI]["AF"]["set"].add(self.decode_AF_freq(array[5])) + try: + self.RDS_data[PI]["AF"]["set"].remove(0)#remove control characters + except KeyError: + pass name_list=list(self.RDS_data[PI]["PSN"]) if (name_list[adr*2:adr*2+2]==list(segment)):#segment already there @@ -706,7 +726,8 @@ class rds_parser_table_qt(gr.sync_block): if (not self.RDS_data[PI]["PSN_valid"][i]): valid = False if(valid): - textcolor="black" + #textcolor="black" + textcolor=""#use default color (white if background is black) if not self.RDS_data[PI]["internals"]["last_valid_psn"]==self.RDS_data[PI]["PSN"]:#ignore duplicates t=(str(datetime.now()),PI,self.RDS_data[PI]["PSN"],"PSN_valid",self.RDS_data[PI]["PSN"]) db.execute("INSERT INTO data (time,PI,PSN,dataType,data) VALUES (?,?,?,?,?)",t) @@ -800,7 +821,8 @@ class rds_parser_table_qt(gr.sync_block): if (not self.RDS_data[PI]["RT_valid"][i]): self.RDS_data[PI]["RT_all_valid"] = False if(self.RDS_data[PI]["RT_all_valid"]): - textcolor="black" + #textcolor="black" + textcolor=""#use default color (white if background is black) l=list(self.RDS_data[PI]["RT"]) rt="".join(l[0:text_end])#remove underscores(default symbol) after line end marker if not self.RDS_data[PI]["internals"]["last_valid_rt"]==rt:#ignore duplicates @@ -820,7 +842,7 @@ class rds_parser_table_qt(gr.sync_block): rtcol=self.colorder.index('text') self.signals.DataUpdateEvent.emit({'col':rtcol,'row':port,'PI':PI,'string':formatted_text}) - #code.interact(local=locals()) + elif (groupType == "3A"):#ODA announcements (contain application ID "AID") AID=(array[6]<<8)|(array[7])#combine 2 bytes into 1 block app_data=(array[4]<<8)|(array[5])#content defined by ODA-app @@ -1038,6 +1060,7 @@ class rds_parser_table_qt(gr.sync_block): if not self.RDS_data.has_key(PI_ON): self.init_data_for_PI(PI_ON) self.RDS_data[PI_ON]["TP"]=TP_ON + self.PI_dict[PI_ON]=0#initialize dict, even if no packets received if self.log: print("found station %s via EON on station %s"%(PI_ON,PI)) if not self.RDS_data[PI]["EON"].has_key(PI_ON): @@ -1070,7 +1093,8 @@ class rds_parser_table_qt(gr.sync_block): if (not self.RDS_data[PI_ON]["PSN_valid"][i]): valid = False if(valid): - textcolor="black" + #textcolor="black" + textcolor=""#use default color (white if background is black) else: textcolor="gray" @@ -1121,7 +1145,7 @@ class rds_parser_table_qt(gr.sync_block): printdelay=500 self.printcounter+=0#printing disabled if self.printcounter == printdelay and self.debug: - #code.interact(local=locals()) + for key in self.RDS_data: if self.RDS_data[key].has_key("PSN"): psn=self.RDS_data[key]["PSN"] @@ -1137,7 +1161,7 @@ class rds_parser_table_qt(gr.sync_block): #db.commit() #db.close() - #pr.disable() + #pr.disable() #disabled-internal-profiling #end of handle_msg def print_tmc_msg(self,tmc_msg): try: @@ -1174,13 +1198,13 @@ class rds_parser_table_qt(gr.sync_block): 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) - #code.interact(local=locals()) + except Exception as e: print(e) raise #print("line 1064") - #code.interact(local=locals()) + except KeyError: #print("location '%i' not found"%tmc_location) pass @@ -1216,24 +1240,30 @@ class rds_parser_table_qt(gr.sync_block): 0b1001:u"âäêëîïôöûüñçş??ij", 0b1100:u"ÁÀÉÈÍÌÓÒÚÙŘČŠŽĐĿ", 0b1101:u"ÂÄÊËÎÏÔÖÛÜřčšžđŀ"} - charlist=list(charstring) + #charlist=list(charstring) + return_string="" for i,char in enumerate(charstring): - #code.interact(local=locals()) + if ord(char)<= 0b01111111: - charlist[i]=char #use ascii + #charlist[i]=char #use ascii + return_string+=char else: #split byte alnr=(ord(char)&0xF0 )>>4 #upper 4 bit - index=ord(char)&0x0F #lower 4 bit - #code.interact(local=locals()) + index=ord(char)&0x0F #lower 4 bit try: - charlist[i]=alphabet[alnr][index] + #charlist[i]=alphabet[alnr][index] + return_string+=alphabet[alnr][index] except KeyError: - charlist[i]='?'#symbol not decoded #TODO + return_string+="?%02X?"%ord(char) + #charlist[i]='?'#symbol not decoded #TODO pass - return "".join(charlist) + #return "".join(charlist) + return return_string def color_text(self, text, start,end,textcolor,segmentcolor): - formatted_text="%s%s%s"% (textcolor,text[:start],segmentcolor,text[start:end],textcolor,text[end:]) + #formatted_text="%s%s%s"% (textcolor,text[:start],segmentcolor,text[start:end],textcolor,text[end:]) + #formatted_text="%s%s%s"% (textcolor,text[:start],segmentcolor,text[start:end],textcolor,text[end:]) + formatted_text=("%s"*3)% (textcolor,text[:start],segmentcolor,text[start:end],textcolor,text[end:]) return formatted_text class rds_parser_table_qt_Widget(QtGui.QWidget): def __init__(self, signals,label,tableobj): @@ -1244,58 +1274,24 @@ class rds_parser_table_qt_Widget(QtGui.QWidget): """ Creates the QT Range widget """ QtGui.QWidget.__init__(self) layout = Qt.QVBoxLayout() - self.label = Qt.QLabel(label) - layout.addWidget(self.label) + #self.label = Qt.QLabel(label) + #layout.addWidget(self.label)#title of table disabled to save space + #layout.addWidget(self.label) self.setLayout(layout) #self.decoder_to_PI={} self.PI_to_row={} self.table=QtGui.QTableWidget(self) - rowcount=2 + rowcount=0 self.table.setRowCount(rowcount) self.table.setColumnCount(9) self.table.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers) #disallow editing - #Data - empty_text32='________________________________' - empty_text64='________________________________________________________________' - #empty_text64='\xe4'*64 - self.data = {'ID':[ QtGui.QLabel() for i in range(rowcount)], - 'freq':[ QtGui.QLabel() for i in range(rowcount)], - 'name':[ QtGui.QLabel() for i in range(rowcount)], - 'PTY':[ QtGui.QLabel() for i in range(rowcount)], - 'AF':[ QtGui.QLabel() for i in range(rowcount)], - 'time':[ QtGui.QLabel() for i in range(rowcount)], - 'text':[ QtGui.QLabel("_"*64) for i in range(rowcount)], - 'quality':[ QtGui.QLabel() for i in range(rowcount)], - 'buttons':[]} - #Enter data onto Table + self.colorder=['ID','freq','name','PTY','AF','time','text','quality','buttons'] - horHeaders = [] - for n, key in enumerate(self.colorder): - #for n, key in enumerate(sorted(self.data.keys())): - horHeaders.append(key) - for m, item in enumerate(self.data[key]): - if type(item)==int:#convert ints to strings - newitem = QtGui.QTableWidgetItem(str(item)) - self.table.setItem(m, n, newitem) - elif isinstance(item,QtGui.QLabel): - self.table.setCellWidget(m,n,item) - else: - newitem = QtGui.QTableWidgetItem(item) - self.table.setItem(m, n, newitem) - for i in range(rowcount):#create buttons - button=QtGui.QPushButton("getDetails") - self.table.setCellWidget(i,self.table.columnCount()-1,button) - button.clicked.connect(functools.partial(self.getDetails, row=i)) - #button.clicked.connect(self.getDetails) - #Add Header - layout.addWidget(self.label) + ##button.clicked.connect(self.getDetails) + layout.addWidget(self.table) - - self.table.setHorizontalHeaderLabels(horHeaders) + self.table.setHorizontalHeaderLabels(self.colorder) #self.table.setMaximumHeight(300)#TODO use dynamic value - - - button_layout = Qt.QHBoxLayout() codebutton = QtGui.QPushButton("code.interact") @@ -1337,6 +1333,22 @@ class rds_parser_table_qt_Widget(QtGui.QWidget): font.setPointSize(10) self.lastResizeTime=0 layout.addWidget(self.logOutput) + self.clip = QtGui.QApplication.clipboard() + #self.cb.clear(mode=cb.Clipboard ) + #self.cb.setText("Clipboard Text", mode=cb.Clipboard) + def keyPressEvent(self, e): + if (e.modifiers() & QtCore.Qt.ControlModifier): + selected = self.table.selectedRanges().pop() + selected.leftColumn() + selected.topRow() + if e.key() == QtCore.Qt.Key_C: #copy + try: + qs = self.table.cellWidget(selected.topRow(),selected.leftColumn()).text()#get QString from table + s=re.sub("<.*?>","", str(qs))#remove html tags + self.clip.setText(s) + except Exception as e: + print(e) + print("no text, cant copy") def insert_empty_row(self): rowPosition = self.table.rowCount() self.table.insertRow(rowPosition) @@ -1441,7 +1453,7 @@ class rds_parser_table_qt_Widget(QtGui.QWidget): layout=Qt.QVBoxLayout() layout.addWidget(l) view.setLayout(layout) - #code.interact(local=locals()) + view.exec_() def getDetails(self,row): PIcol=self.colorder.index('ID') @@ -1474,7 +1486,7 @@ class rds_parser_table_qt_Widget(QtGui.QWidget): l.setWordWrap(True) #l=QtGui.QLabel("Data:") view.layout().addWidget(l) - #code.interact(local=locals()) + view.exec_() def onCLick(self): print("button clicked") @@ -1483,18 +1495,14 @@ if __name__ == "__main__": from PyQt4 import Qt import sys - # def valueChanged(frequency): - # print("Value updated - " + str(frequency)) app = Qt.QApplication(sys.argv) - # widget = RangeWidget(Range(0, 100, 10, 1, 100), valueChanged, "Test", "counter_slider", int) mainobj= rds_parser_table_qt_Signals() #mainobj=None widget = rds_parser_table_qt_Widget(mainobj,"TestLabel") widget.show() widget.setWindowTitle("Test Qt gui") widget.setGeometry(200,200,600,300) - #code.interact(local=locals()) sys.exit(app.exec_()) widget = None