Code:
#!/usr/bin/env python
##################################################
# Gnuradio Python Flow Graph
# Title: Sradio
# Author: Keith Nuesslein
# Description: Sradio 1.01 FM radio with seek tuning
# Generated: Fri Sep 7 10:17:42 2012
##################################################
from PyQt4 import Qt
from gnuradio import audio
from gnuradio import blks2
from gnuradio import eng_notation
from gnuradio import gr
from gnuradio.eng_option import eng_option
from gnuradio.gr import firdes
from optparse import OptionParser
import PyQt4.Qwt5 as Qwt
import baz
import sys
import threading
import time
class Sradio(gr.top_block, Qt.QWidget):
def __init__(self, squelchThreshold=-60, deemphasis=75e-6, defaultStation=96.7, lpfCutoff=100000, magAlpha=0.001, sdrFreqCorr=82, srate=1.920e6, rfGain=1, epsilon=.01, scanThreshold=0.3):
gr.top_block.__init__(self, "Sradio")
Qt.QWidget.__init__(self)
self.setWindowTitle("Sradio")
self.setWindowIcon(Qt.QIcon.fromTheme('gnuradio-grc'))
self.top_scroll_layout = Qt.QVBoxLayout()
self.setLayout(self.top_scroll_layout)
self.top_scroll = Qt.QScrollArea()
self.top_scroll.setFrameStyle(Qt.QFrame.NoFrame)
self.top_scroll_layout.addWidget(self.top_scroll)
self.top_scroll.setWidgetResizable(True)
self.top_widget = Qt.QWidget()
self.top_scroll.setWidget(self.top_widget)
self.top_layout = Qt.QVBoxLayout(self.top_widget)
self.top_grid_layout = Qt.QGridLayout()
self.top_layout.addLayout(self.top_grid_layout)
##################################################
# Parameters
##################################################
self.squelchThreshold = squelchThreshold
self.deemphasis = deemphasis
self.defaultStation = defaultStation
self.lpfCutoff = lpfCutoff
self.magAlpha = magAlpha
self.sdrFreqCorr = sdrFreqCorr
self.srate = srate
self.rfGain = rfGain
self.epsilon = epsilon
self.scanThreshold = scanThreshold
##################################################
# Variables
##################################################
self.probe = probe = 0
self.f = f = defaultStation
self.vol = vol = 1
self.variable_qtgui_label_0 = variable_qtgui_label_0 = "%7.3f %23s%6.1f Mhz" % (probe," ",f)
self.variable_qtgui_chooser_0 = variable_qtgui_chooser_0 = 1
self.samp_rate = samp_rate = srate
self.dec = dec = 8
##################################################
# Blocks
##################################################
self._vol_layout = Qt.QVBoxLayout()
self._vol_label = Qt.QLabel("Volume")
self._vol_slider = Qwt.QwtSlider(None, Qt.Qt.Horizontal, Qwt.QwtSlider.BottomScale, Qwt.QwtSlider.BgSlot)
self._vol_slider.setRange(0, 10, .5)
self._vol_slider.setValue(self.vol)
self._vol_slider.setMinimumWidth(200)
self._vol_slider.valueChanged.connect(self.set_vol)
self._vol_label.setAlignment(Qt.Qt.AlignBottom | Qt.Qt.AlignHCenter)
self._vol_layout.addWidget(self._vol_label)
self._vol_layout.addWidget(self._vol_slider)
self.top_grid_layout.addLayout(self._vol_layout, 2,10)
self._f_layout = Qt.QVBoxLayout()
self._f_label = Qt.QLabel("FM Tuner")
self._f_slider = Qwt.QwtSlider(None, Qt.Qt.Horizontal, Qwt.QwtSlider.BottomScale, Qwt.QwtSlider.BgSlot)
self._f_slider.setRange(87.5, 108.1, .2)
self._f_slider.setValue(self.f)
self._f_slider.setMinimumWidth(200)
self._f_slider.valueChanged.connect(self.set_f)
self._f_label.setAlignment(Qt.Qt.AlignBottom | Qt.Qt.AlignHCenter)
self._f_layout.addWidget(self._f_label)
self._f_layout.addWidget(self._f_slider)
self.top_grid_layout.addLayout(self._f_layout, 0,1,1,10)
self.avgmag2 = gr.probe_avg_mag_sqrd_c(squelchThreshold, magAlpha)
self._variable_qtgui_label_0_tool_bar = Qt.QToolBar(self)
self._variable_qtgui_label_0_tool_bar.addWidget(Qt.QLabel("Signal"+": "))
self._variable_qtgui_label_0_label = Qt.QLabel(str(self.variable_qtgui_label_0))
self._variable_qtgui_label_0_tool_bar.addWidget(self._variable_qtgui_label_0_label)
self.top_grid_layout.addWidget(self._variable_qtgui_label_0_tool_bar, 3,1,1,10)
self._variable_qtgui_chooser_0_options = (0, 1, 2, )
self._variable_qtgui_chooser_0_labels = ("DOWN", "STOP", "UP", )
self._variable_qtgui_chooser_0_group_box = Qt.QGroupBox("Seekmode")
self._variable_qtgui_chooser_0_box = Qt.QHBoxLayout()
self._variable_qtgui_chooser_0_button_group = Qt.QButtonGroup()
self._variable_qtgui_chooser_0_group_box.setLayout(self._variable_qtgui_chooser_0_box)
for i, label in enumerate(self._variable_qtgui_chooser_0_labels):
radio_button = Qt.QRadioButton(label)
self._variable_qtgui_chooser_0_box.addWidget(radio_button)
self._variable_qtgui_chooser_0_button_group.addButton(radio_button, i)
self._variable_qtgui_chooser_0_callback = lambda i: self._variable_qtgui_chooser_0_button_group.button(self._variable_qtgui_chooser_0_options.index(i)).setChecked(True)
self._variable_qtgui_chooser_0_callback(self.variable_qtgui_chooser_0)
self._variable_qtgui_chooser_0_button_group.buttonClicked[int].connect(
lambda i: self.set_variable_qtgui_chooser_0(self._variable_qtgui_chooser_0_options[i]))
self.top_grid_layout.addWidget(self._variable_qtgui_chooser_0_group_box, 2,1)
self.rtl2832_source_0 = baz.rtl_source_c(defer_creation=True, output_size=gr.sizeof_gr_complex)
self.rtl2832_source_0.set_verbose(True)
self.rtl2832_source_0.set_vid(0x0)
self.rtl2832_source_0.set_pid(0x0)
self.rtl2832_source_0.set_tuner_name("e4k")
self.rtl2832_source_0.set_default_timeout(0)
self.rtl2832_source_0.set_use_buffer(True)
self.rtl2832_source_0.set_fir_coefficients(([]))
self.rtl2832_source_0.set_read_length(0)
if self.rtl2832_source_0.create() == False: raise Exception("Failed to create RTL2832 Source: rtl2832_source_0")
self.rtl2832_source_0.set_sample_rate(samp_rate)
self.rtl2832_source_0.set_frequency(f*1e6)
self.rtl2832_source_0.set_auto_gain_mode(False)
self.rtl2832_source_0.set_relative_gain(True)
self.rtl2832_source_0.set_gain(rfGain)
def scan(scanmode):
if scanmode == 1: return
self.set_squelchThreshold(10) # mute while scanning
step = 0.2 if scanmode == 2 else -0.2
f = self.f + step
if f > 108: f = 87.5
if f < 87.5: f = 108.1 # must be odd freq
self.set_f(f)
# find greatest level in scanmode direction (assumes positive slope)
def scantune(scanmode,val):
print "\nscantune %4.1f %f" % (self.f,val)
self.set_variable_qtgui_chooser_0(1)
step = 0.2 if scanmode == 2 else -0.2
while True:
f=self.f + step
self.set_f(f)
time.sleep(0.5)
newval = self.avgmag2.level()
print "%4.2f %f %4.2f %f" % (f-step,val,f,newval)
if (newval <= val + self.epsilon): break
val = newval
f=self.f - step
self.set_f(f)
def _probe_probe():
while True:
val = self.avgmag2.level()
if (val > scanThreshold) and (scanmode <> 1) and (oldval < val):
scantune(scanmode,val)
scanmode = 1
else:
self.set_squelchThreshold(squelchThreshold) # unmute
scanmode = self.get_variable_qtgui_chooser_0()
scan(scanmode)
oldval = val
try: self.set_probe(val)
except AttributeError, e: pass
time.sleep(1.0/(5))
_probe_thread = threading.Thread(target=_probe_probe)
_probe_thread.daemon = True
self.low_pass_filter_1 = gr.fir_filter_fff(5, firdes.low_pass(
vol*50, samp_rate/dec, 15000, 1000, firdes.WIN_HAMMING, 6.76))
self.low_pass_filter_0 = gr.fir_filter_ccf(dec, firdes.low_pass(
4, samp_rate, lpfCutoff, 8000, firdes.WIN_HAMMING, 6.76))
self.gr_simple_squelch_cc_0 = gr.simple_squelch_cc(squelchThreshold, magAlpha)
self.blks2_wfm_rcv_0 = blks2.wfm_rcv(
quad_rate=samp_rate/dec,
audio_decimation=1,
)
self.blks2_fm_deemph_0 = blks2.fm_deemph(fs=samp_rate/dec, tau=deemphasis)
self.audio_sink_0 = audio.sink(48000, "pulse", True)
##################################################
# Connections
##################################################
self.connect((self.low_pass_filter_0, 0), (self.avgmag2, 0))
self.connect((self.gr_simple_squelch_cc_0, 0), (self.blks2_wfm_rcv_0, 0))
self.connect((self.blks2_wfm_rcv_0, 0), (self.blks2_fm_deemph_0, 0))
self.connect((self.low_pass_filter_0, 0), (self.gr_simple_squelch_cc_0, 0))
self.connect((self.low_pass_filter_1, 0), (self.audio_sink_0, 0))
self.connect((self.blks2_fm_deemph_0, 0), (self.low_pass_filter_1, 0))
self.connect((self.rtl2832_source_0, 0), (self.low_pass_filter_0, 0))
_probe_thread.start()
def get_squelchThreshold(self):
return self.squelchThreshold
def set_squelchThreshold(self, squelchThreshold):
self.squelchThreshold = squelchThreshold
self.avgmag2.set_threshold(self.squelchThreshold)
self.gr_simple_squelch_cc_0.set_threshold(self.squelchThreshold)
def get_deemphasis(self):
return self.deemphasis
def set_deemphasis(self, deemphasis):
self.deemphasis = deemphasis
def get_defaultStation(self):
return self.defaultStation
def set_defaultStation(self, defaultStation):
self.defaultStation = defaultStation
self.set_f(self.defaultStation)
def get_lpfCutoff(self):
return self.lpfCutoff
def set_lpfCutoff(self, lpfCutoff):
self.lpfCutoff = lpfCutoff
self.low_pass_filter_0.set_taps(firdes.low_pass(4, self.samp_rate, self.lpfCutoff, 8000, firdes.WIN_HAMMING, 6.76))
def get_magAlpha(self):
return self.magAlpha
def set_magAlpha(self, magAlpha):
self.magAlpha = magAlpha
self.avgmag2.set_alpha(self.magAlpha)
self.gr_simple_squelch_cc_0.set_alpha(self.magAlpha)
def get_sdrFreqCorr(self):
return self.sdrFreqCorr
def set_sdrFreqCorr(self, sdrFreqCorr):
self.sdrFreqCorr = sdrFreqCorr
def get_srate(self):
return self.srate
def set_srate(self, srate):
self.srate = srate
self.set_samp_rate(self.srate)
def get_rfGain(self):
return self.rfGain
def set_rfGain(self, rfGain):
self.rfGain = rfGain
self.rtl2832_source_0.set_gain(self.rfGain)
def get_epsilon(self):
return self.epsilon
def set_epsilon(self, epsilon):
self.epsilon = epsilon
def get_scanThreshold(self):
return self.scanThreshold
def set_scanThreshold(self, scanThreshold):
self.scanThreshold = scanThreshold
def get_probe(self):
return self.probe
def set_probe(self, probe):
self.probe = probe
self.set_variable_qtgui_label_0("%7.3f %23s%6.1f Mhz" % (self.probe," ",self.f) )
def get_f(self):
return self.f
def set_f(self, f):
self.f = f
self.set_variable_qtgui_label_0("%7.3f %23s%6.1f Mhz" % (self.probe," ",self.f) )
self._f_slider.setValue(self.f)
self.rtl2832_source_0.set_frequency(self.f*1e6)
def get_vol(self):
return self.vol
def set_vol(self, vol):
self.vol = vol
self._vol_slider.setValue(self.vol)
self.low_pass_filter_1.set_taps(firdes.low_pass(self.vol*50, self.samp_rate/self.dec, 15000, 1000, firdes.WIN_HAMMING, 6.76))
def get_variable_qtgui_label_0(self):
return self.variable_qtgui_label_0
def set_variable_qtgui_label_0(self, variable_qtgui_label_0):
self.variable_qtgui_label_0 = variable_qtgui_label_0
self._variable_qtgui_label_0_label.setText(str(self.variable_qtgui_label_0))
def get_variable_qtgui_chooser_0(self):
return self.variable_qtgui_chooser_0
def set_variable_qtgui_chooser_0(self, variable_qtgui_chooser_0):
self.variable_qtgui_chooser_0 = variable_qtgui_chooser_0
self._variable_qtgui_chooser_0_callback(self.variable_qtgui_chooser_0)
def get_samp_rate(self):
return self.samp_rate
def set_samp_rate(self, samp_rate):
self.samp_rate = samp_rate
self.low_pass_filter_0.set_taps(firdes.low_pass(4, self.samp_rate, self.lpfCutoff, 8000, firdes.WIN_HAMMING, 6.76))
self.low_pass_filter_1.set_taps(firdes.low_pass(self.vol*50, self.samp_rate/self.dec, 15000, 1000, firdes.WIN_HAMMING, 6.76))
self.rtl2832_source_0.set_sample_rate(self.samp_rate)
def get_dec(self):
return self.dec
def set_dec(self, dec):
self.dec = dec
self.low_pass_filter_1.set_taps(firdes.low_pass(self.vol*50, self.samp_rate/self.dec, 15000, 1000, firdes.WIN_HAMMING, 6.76))
if __name__ == '__main__':
parser = OptionParser(option_class=eng_option, usage="%prog: [options]")
parser.add_option("-t", "--squelchThreshold", dest="squelchThreshold", type="intx", default=-60,
help="Set squelchThreshold [default=%default]")
parser.add_option("-e", "--deemphasis", dest="deemphasis", type="eng_float", default=eng_notation.num_to_str(75e-6),
help="Set deemphasis [default=%default]")
parser.add_option("-d", "--defaultStation", dest="defaultStation", type="eng_float", default=eng_notation.num_to_str(96.7),
help="Set (Mhz) [default=%default]")
parser.add_option("-l", "--lpfCutoff", dest="lpfCutoff", type="long", default=100000,
help="Set rf lowpass filter [default=%default]")
parser.add_option("-m", "--magAlpha", dest="magAlpha", type="eng_float", default=eng_notation.num_to_str(0.001),
help="Set signal mag alpha [default=%default]")
parser.add_option("-c", "--sdrFreqCorr", dest="sdrFreqCorr", type="intx", default=82,
help="Set sdrFreqCorr [default=%default]")
parser.add_option("-r", "--srate", dest="srate", type="eng_float", default=eng_notation.num_to_str(1.920e6),
help="Set srate [default=%default]")
parser.add_option("-g", "--rfGain", dest="rfGain", type="eng_float", default=eng_notation.num_to_str(1),
help="Set rfGain [default=%default]")
parser.add_option("-p", "--epsilon", dest="epsilon", type="eng_float", default=eng_notation.num_to_str(.01),
help="Set min diff [default=%default]")
parser.add_option("-s", "--scanThreshold", dest="scanThreshold", type="eng_float", default=eng_notation.num_to_str(0.3),
help="Set scan signal detect threshold [default=%default]")
(options, args) = parser.parse_args()
qapp = Qt.QApplication(sys.argv)
tb = Sradio(squelchThreshold=options.squelchThreshold, deemphasis=options.deemphasis, defaultStation=options.defaultStation, lpfCutoff=options.lpfCutoff, magAlpha=options.magAlpha, sdrFreqCorr=options.sdrFreqCorr, srate=options.srate, rfGain=options.rfGain, epsilon=options.epsilon, scanThreshold=options.scanThreshold)
tb.start()
tb.show()
qapp.exec_()
tb.stop()
The Sradio.py application depends on signal strength comparisons so disabling agc is highly desirable, however, in my case the