#!/usr/bin/env python
##################################################
# Gnuradio Python Flow Graph
# Title: Automatic Frequency Compensation Demo
# Author: Martin Dudok van Heel
# Description: FM AFC demo using variable sink
# Generated: Wed Mar 17 02:13:43 2010
##################################################

from gnuradio import blks2
from gnuradio import eng_notation
from gnuradio import gr
from gnuradio import window
from gnuradio.eng_option import eng_option
from gnuradio.gr import firdes
from gnuradio.wxgui import fftsink2
from gnuradio.wxgui import forms
from gnuradio.wxgui import scopesink2
from grc_gnuradio import blks2 as grc_blks2
from grc_gnuradio import wxgui as grc_wxgui
from optparse import OptionParser
import math
import wx

class mdvh_AFC_FM_demo_noaa_weather_satellite(grc_wxgui.top_block_gui):

	def __init__(self):
		grc_wxgui.top_block_gui.__init__(self, title="Automatic Frequency Compensation Demo")
		_icon_path = "/usr/local/share/icons/hicolor/32x32/apps/gnuradio-grc.png"
		self.SetIcon(wx.Icon(_icon_path, wx.BITMAP_TYPE_ANY))

		##################################################
		# Variables
		##################################################
		self.AFC_variable = AFC_variable = 0
		self.AFC_gain = AFC_gain = 1.0e4
		self.samp_rate = samp_rate = 256000
		self.max_dev = max_dev = 18e3
		self.channel_bandwidth = channel_bandwidth = 40e3
		self.AFC_linear = AFC_linear = -AFC_gain*AFC_variable
		self.max_dev_radians_per_sec = max_dev_radians_per_sec = 2.0*math.pi*2.0*max_dev/samp_rate
		self.freq_src = freq_src = 1000
		self.audio_rate = audio_rate = samp_rate/8
		self.alpha = alpha = 0.25*channel_bandwidth * math.pi / samp_rate
		self.AFC_update_rate = AFC_update_rate = 1.0
		self.AFC_log = AFC_log = -1.0*math.copysign(math.log(1+abs(AFC_gain*AFC_variable)),AFC_variable)
		self.AFC_freq_comp = AFC_freq_comp = max_dev*min(max(AFC_linear,-1.0),1.0)
		self.AFC_IIR_alpha = AFC_IIR_alpha = 1.0e-8

		##################################################
		# Controls
		##################################################
		_freq_src_sizer = wx.BoxSizer(wx.VERTICAL)
		self._freq_src_text_box = forms.text_box(
			parent=self.GetWin(),
			sizer=_freq_src_sizer,
			value=self.freq_src,
			callback=self.set_freq_src,
			label='freq_src',
			converter=forms.float_converter(),
			proportion=0,
		)
		self._freq_src_slider = forms.slider(
			parent=self.GetWin(),
			sizer=_freq_src_sizer,
			value=self.freq_src,
			callback=self.set_freq_src,
			minimum=-20000,
			maximum=20000,
			num_steps=100,
			style=wx.SL_HORIZONTAL,
			cast=float,
			proportion=1,
		)
		self.Add(_freq_src_sizer)

		##################################################
		# Blocks
		##################################################
		self.AFC_iir = gr.single_pole_iir_filter_ff(AFC_IIR_alpha, 1)
		self.AFC_variable_sink = grc_blks2.variable_sink_f(
			vlen=1,
			decim=int(samp_rate/AFC_update_rate),
			callback=self.set_AFC_variable,
		)
		self.fftsink = fftsink2.fft_sink_c(
			self.GetWin(),
			baseband_freq=0,
			y_per_div=10,
			y_divs=10,
			ref_level=50,
			ref_scale=2.0,
			sample_rate=samp_rate,
			fft_size=1024,
			fft_rate=30,
			average=False,
			avg_alpha=None,
			title="FFT Plot",
			peak_hold=False,
		)
		self.Add(self.fftsink.win)
		self.freq_compensating_channel_filter = gr.freq_xlating_fir_filter_ccf(1, (gr.firdes.low_pass (1.0, samp_rate,(0.5+0.1)*channel_bandwidth, 0.1*channel_bandwidth, gr.firdes.WIN_HANN)), AFC_freq_comp, samp_rate)
		self.nbfm_rx = blks2.nbfm_rx(
			audio_rate=audio_rate,
			quad_rate=samp_rate,
			tau=75e-6,
			max_dev=max_dev,
		)
		self.nbfm_tx = blks2.nbfm_tx(
			audio_rate=audio_rate,
			quad_rate=samp_rate,
			tau=75e-6,
			max_dev=max_dev,
		)
		self.scopesink = scopesink2.scope_sink_f(
			self.GetWin(),
			title="Scope Plot",
			sample_rate=audio_rate,
			v_scale=0.0125,
			v_offset=0,
			t_scale=0,
			ac_couple=False,
			xy_mode=False,
			num_inputs=1,
		)
		self.Add(self.scopesink.win)
		self.src_audio = gr.sig_source_f(audio_rate, gr.GR_COS_WAVE, 2400, 1, (freq_src/20000))
		self.throttle = gr.throttle(gr.sizeof_float*1, samp_rate)

		##################################################
		# Connections
		##################################################
		self.connect((self.nbfm_rx, 0), (self.scopesink, 0))
		self.connect((self.src_audio, 0), (self.throttle, 0))
		self.connect((self.throttle, 0), (self.nbfm_tx, 0))
		self.connect((self.nbfm_rx, 0), (self.AFC_iir, 0))
		self.connect((self.AFC_iir, 0), (self.AFC_variable_sink, 0))
		self.connect((self.nbfm_tx, 0), (self.freq_compensating_channel_filter, 0))
		self.connect((self.freq_compensating_channel_filter, 0), (self.nbfm_rx, 0))
		self.connect((self.freq_compensating_channel_filter, 0), (self.fftsink, 0))

	def set_AFC_variable(self, AFC_variable):
		self.AFC_variable = AFC_variable
		self.set_AFC_linear(-self.AFC_gain*self.AFC_variable)
		self.set_AFC_log(-1.0*math.copysign(math.log(1+abs(self.AFC_gain*self.AFC_variable)),self.AFC_variable))

	def set_AFC_gain(self, AFC_gain):
		self.AFC_gain = AFC_gain
		self.set_AFC_linear(-self.AFC_gain*self.AFC_variable)
		self.set_AFC_log(-1.0*math.copysign(math.log(1+abs(self.AFC_gain*self.AFC_variable)),self.AFC_variable))

	def set_samp_rate(self, samp_rate):
		self.samp_rate = samp_rate
		self.set_alpha(0.25*self.channel_bandwidth * math.pi / self.samp_rate)
		self.fftsink.set_sample_rate(self.samp_rate)
		self.set_max_dev_radians_per_sec(2.0*math.pi*2.0*self.max_dev/self.samp_rate)
		self.freq_compensating_channel_filter.set_taps((gr.firdes.low_pass (1.0, self.samp_rate,(0.5+0.1)*self.channel_bandwidth, 0.1*self.channel_bandwidth, gr.firdes.WIN_HANN)))
		self.AFC_variable_sink.set_decim(int(self.samp_rate/self.AFC_update_rate))
		self.set_audio_rate(self.samp_rate/8)

	def set_max_dev(self, max_dev):
		self.max_dev = max_dev
		self.set_max_dev_radians_per_sec(2.0*math.pi*2.0*self.max_dev/self.samp_rate)
		self.set_AFC_freq_comp(self.max_dev*min(max(self.AFC_linear,-1.0),1.0))

	def set_channel_bandwidth(self, channel_bandwidth):
		self.channel_bandwidth = channel_bandwidth
		self.set_alpha(0.25*self.channel_bandwidth * math.pi / self.samp_rate)
		self.freq_compensating_channel_filter.set_taps((gr.firdes.low_pass (1.0, self.samp_rate,(0.5+0.1)*self.channel_bandwidth, 0.1*self.channel_bandwidth, gr.firdes.WIN_HANN)))

	def set_AFC_linear(self, AFC_linear):
		self.AFC_linear = AFC_linear
		self.set_AFC_freq_comp(self.max_dev*min(max(self.AFC_linear,-1.0),1.0))

	def set_max_dev_radians_per_sec(self, max_dev_radians_per_sec):
		self.max_dev_radians_per_sec = max_dev_radians_per_sec

	def set_freq_src(self, freq_src):
		self.freq_src = freq_src
		self._freq_src_slider.set_value(self.freq_src)
		self._freq_src_text_box.set_value(self.freq_src)
		self.src_audio.set_offset((self.freq_src/20000))

	def set_audio_rate(self, audio_rate):
		self.audio_rate = audio_rate
		self.scopesink.set_sample_rate(self.audio_rate)
		self.src_audio.set_sampling_freq(self.audio_rate)

	def set_alpha(self, alpha):
		self.alpha = alpha

	def set_AFC_update_rate(self, AFC_update_rate):
		self.AFC_update_rate = AFC_update_rate
		self.AFC_variable_sink.set_decim(int(self.samp_rate/self.AFC_update_rate))

	def set_AFC_log(self, AFC_log):
		self.AFC_log = AFC_log

	def set_AFC_freq_comp(self, AFC_freq_comp):
		self.AFC_freq_comp = AFC_freq_comp
		self.freq_compensating_channel_filter.set_center_freq(self.AFC_freq_comp)

	def set_AFC_IIR_alpha(self, AFC_IIR_alpha):
		self.AFC_IIR_alpha = AFC_IIR_alpha
		self.AFC_iir.set_taps(self.AFC_IIR_alpha)

if __name__ == '__main__':
	parser = OptionParser(option_class=eng_option, usage="%prog: [options]")
	(options, args) = parser.parse_args()
	tb = mdvh_AFC_FM_demo_noaa_weather_satellite()
	tb.Run(True)


