RDS decoder module for GNU Radio, that decodes multiple stations simultaneously
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

163 lines
6.1 KiB

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright 2016 <+YOU OR YOUR COMPANY+>.
#
# This is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3, or (at your option)
# any later version.
#
# This software is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this software; see the file COPYING. If not, write to
# the Free Software Foundation, Inc., 51 Franklin Street,
# Boston, MA 02110-1301, USA.
#
import numpy as np
from gnuradio import gr
import code,math,pmt
class max_freq(gr.sync_block):
"""
docstring for block max_freq
"""
def __init__(self, fft_len=1024,num_decoders=4,center_freq=0,samp_rate=0,round_to=100e3,debug=False):
gr.sync_block.__init__(self,
name="max_freq",
in_sig=[(np.float32,fft_len)],
out_sig=None)
self.fft_len=fft_len
self.num_decoders=num_decoders
self.center_freq=center_freq
self.samp_rate=samp_rate
self.snapto=round_to
self.debug=debug
self.last_station_indices=[0]*self.num_decoders
self.message_port_register_out(pmt.intern('out'))
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)
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):
self.center_freq=freq
else:
self.center_freq = int(freq)
def work(self, input_items, output_items):
if self.counter<5:
self.counter+=1
return len(input_items[0])
elif self.searchMode:
self.counter=0
#in0 = input_items[0]
#ii=input_items
carrier_width=2
carrier=self.fft_len/2
numbers=np.delete(input_items[0][0],range(carrier-carrier_width,carrier+carrier_width+1))#read input and disregard center (hackrf LO)
threshold=40# uni
#threshold=60#home
#minimum number of consecutive maximums (in fft domain) to consider signal as station:
#min_consec_max_threshold=1#uni
min_consec_max_threshold=3#home
fuzzyness=2#uni
#fuzzyness=10#home
#TODO: what if no numbers over threshold?
#TODO auto threshold
#max_indices=[[421, 428, 429, 430, 431, 432, 433, 434, 436, 437, 438, 831, 832, 837, 840, 841, 842, 843, 844, 845, 846, 847, 848, 849, 850, 851,852, 853, 854, 855, 856, 857]]
max_indices=np.where(numbers>threshold)
station_indices=[]
try:
last_index=max_indices[0][0]
except IndexError:
last_index=0
count=1#counts number of consecutive maximums
threshold_reached=False
# max_indices[0].append(0)#to detect last station
max_indices=np.append(max_indices,0)#to detect last station
#try:
#max_indices.remove(self.fft_len/2)#suppress local oscillator of hackrf
#except ValueError:
#pass
for i in max_indices:
if abs(i-last_index) <= fuzzyness:
count+=i-last_index
else:#last streak ended
if(threshold_reached):
station_indices.append(last_index-int(count/2))#use center of max-streak
threshold_reached=False
count=1
else:#last streak didn't reach threshold -> no station
count=1
if count>=min_consec_max_threshold:
threshold_reached=True
last_index=i
#sort station_indices by signal strength (dont bother decoding quiet stations)
station_indices_sorted=sorted(station_indices,reverse=True,key=lambda elem:numbers[elem])
#prevents back and forth switching if two station have similar signal strength
station_indices_trunc=list(station_indices_sorted)#copy list
del station_indices_trunc[self.num_decoders:]#remove non decodable incidices
station_indices_tune=[0]*self.num_decoders
same_station_threshold=3
new_stations=[]
for new_freq in station_indices_trunc:
added=False
for i,old_freq in enumerate(self.last_station_indices):
if abs(old_freq-new_freq)<same_station_threshold:
station_indices_tune[i]=new_freq
added=True
if not added:
new_stations.append(new_freq)
#print("tune1:%s"%station_indices_tune)
#print("new_1 %s"%new_stations)
for i,tune_freq in enumerate(station_indices_tune):
if tune_freq == 0 and len(new_stations)>0:
station_indices_tune[i]=new_stations.pop()
#print("tune2:%s"%station_indices_tune)
#print("new_2 %s"%new_stations)
self.last_station_indices=station_indices_tune#save current stations to compare againts next
station_strength=[]
station_freqs=[]
#index to freq:
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))
for i in range(0,min(self.num_decoders,len(station_freqs))):
msg_string=str(i+1)+" "+str(station_freqs[i])
send_pmt = pmt.string_to_symbol(msg_string)
self.message_port_pub(pmt.intern('out'), send_pmt)
if self.debug:
print(max_indices)
print(station_indices_sorted)
print(station_indices_tune)
print(station_strength)
print(station_freqs)
return len(input_items[0])
else:
return len(input_items[0])