#!/usr/bin/env python
#
# Copyright 2004,2005 Free Software Foundation, Inc.
# 
# This file is part of GNU Radio
# 
# GNU Radio 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 2, or (at your option)
# any later version.
# 
# GNU Radio 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 GNU Radio; see the file COPYING.  If not, write to
# the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
# Boston, MA 02111-1307, USA.
# 

from gnuradio import gr, gru
from gnuradio import usrp
from gnuradio import eng_notation
from gnuradio.eng_option import eng_option
from gnuradio.wxgui import stdgui, fftsink, waterfallsink, scopesink, form, slider
from optparse import OptionParser
import wx
import sys


class app_flow_graph(stdgui.gui_flow_graph):
    def __init__(self, frame, panel, vbox, argv):
        stdgui.gui_flow_graph.__init__(self)

        self.frame = frame
        self.panel = panel
        
        parser = OptionParser(option_class=eng_option)
        parser.add_option("-g", "--gain", type="eng_float", default=None,
                          help="set gain in dB (default is midpoint)")
        parser.add_option("-f", "--freq", type="eng_float", default=0.0,
                          help="set frequency to FREQ", metavar="FREQ")
        (options, args) = parser.parse_args()
        if len(args) != 0:
            parser.print_help()
            sys.exit(1)
            
        self.show_debug_info = True
        
        # build the graph

        self.u = usrp.source_c(decim_rate=250)
        self.u.set_mux(usrp.determine_rx_mux_value(self.u, (0, 0)))

        # Turn off auto dc offset
        self.u.set_dc_offset_cl_enable(0x0, 0xf) 
            
        # determine the daughterboard subdevice we're using
        self.subdev = usrp.selected_subdev(self.u, (0, 0))

        input_rate = self.u.adc_freq() / self.u.decim_rate() # 64 MS/s // 250

       	# Let's decimate it some more
       	if_rate = 10000				# reduce sampling down to 
       	if_decim = int ( input_rate // if_rate )
        if_rate = input_rate // if_decim	# recalculate because interger effects 
	
       	# Create decimate filter
        chan_filter_coeffs = gr.firdes.low_pass (1.0,		# gain
						 if_rate,	# sampling rate
						 4e3,		# low pass cutoff freq
						 1.0e3,		# width of trans. band
						 gr.firdes.WIN_HANN)

       	print "len(rx_chan_coeffs) =", len(chan_filter_coeffs)
        # Decimating Channel complex in and out, float taps
       	self.chan_filter = gr.fir_filter_ccf(if_decim, chan_filter_coeffs)

       	# Translate it down
       	base_decim = 1
       	base_rate = if_rate /base_decim

       	# Create coefficiant
        base_filter_coeffs = gr.firdes.low_pass(1.0,		# gain
						base_rate,	# sampling rate
						800,		# low pass cutoff freq
						50,		# width of trans. band
						gr.firdes.WIN_HANN)
       	print "len(base_chan_coeffs) =", len(base_filter_coeffs)
       	fcar = 3600.0			# Carrier Frequency
       	self.base_band = gr.freq_xlating_fir_filter_ccf(base_decim, base_filter_coeffs,
							fcar, base_rate)


        self.scope = scopesink.scope_sink_f (self, panel, sample_rate=input_rate)
	
        # Using default values in gmsk.py
        spb = 4
        omega = None
        gain_mu = 0.03
        mu = 0.5
        omega_relative_limit = 0.000200
        freq_error = 0.0
        self.spb = spb
        if omega is None:
	       	omega = spb*(1+freq_error)
        gain_omega = 0.25*gain_mu*gain_mu

       	# Low pass the output to allow the removal of any
       	# DS offset resulting from frequency offset
       	alpha = 0.0002
       	self.freq_offset = gr.single_pole_iir_filter_ff(alpha)
       	self.sub = gr.sub_ff()
       	
       	# convert to magnitude
       	self.c2m = gr.complex_to_mag()
       	self.clock_recovery=gr.clock_recovery_mm_ff(omega, gain_omega,
       							mu, gain_mu, omega_relative_limit)
       	self.connect(self.u, self.chan_filter, self.base_band, self.c2m)
       	self.connect(self.c2m, (self.sub, 0))
       	self.connect(self.c2m, self.freq_offset, (self.sub, 1))
       	self.connect(self.sub, self.clock_recovery)
       	self.connect(self.clock_recovery, self.scope)

       	self._build_gui(vbox)

        # set initial values

        if options.gain is None:
            # if no gain was specified, use the mid-point in dB
            g = self.subdev.gain_range()
            options.gain = float(g[0]+g[1])/2

        if options.freq is None:
            # if no freq was specified, use the mid-point
            r = self.subdev.freq_range()
            options.freq = float(r[0]+r[1])/2

        self.set_gain(options.gain)

        if not(self.set_freq(options.freq)):
            self._set_status_msg("Failed to set initial frequency")

        if self.show_debug_info:
            self.myform['decim'].set_value(self.u.decim_rate())
            self.myform['fs@usb'].set_value(self.u.adc_freq() / self.u.decim_rate())
            self.myform['dbname'].set_value(self.subdev.name())
                        

    def _set_status_msg(self, msg):
        self.frame.GetStatusBar().SetStatusText(msg, 0)

    def _build_gui(self, vbox):

        def _form_set_freq(kv):
            return self.set_freq(kv['freq'])
            
        vbox.Add(self.scope.win, 10, wx.EXPAND)
        
        # add control area at the bottom
        self.myform = myform = form.form()
        hbox = wx.BoxSizer(wx.HORIZONTAL)
        hbox.Add((5,0), 0, 0)
        myform['freq'] = form.float_field(
            parent=self.panel, sizer=hbox, label="Center freq", weight=1,
            callback=myform.check_input_and_call(_form_set_freq, self._set_status_msg))

        hbox.Add((5,0), 0, 0)
        g = self.subdev.gain_range()
        myform['gain'] = form.slider_field(parent=self.panel, sizer=hbox, label="Gain",
                                           weight=3,
                                           min=int(g[0]), max=int(g[1]),
                                           callback=self.set_gain)

        hbox.Add((5,0), 0, 0)
        vbox.Add(hbox, 0, wx.EXPAND)

        self._build_subpanel(vbox)

    def _build_subpanel(self, vbox_arg):
        # build a secondary information panel (sometimes hidden)

        # FIXME figure out how to have this be a subpanel that is always
        # created, but has its visibility controlled by foo.Show(True/False)
        
        if not(self.show_debug_info):
            return

        panel = self.panel
        vbox = vbox_arg
        myform = self.myform

        #panel = wx.Panel(self.panel, -1)
        #vbox = wx.BoxSizer(wx.VERTICAL)

        hbox = wx.BoxSizer(wx.HORIZONTAL)
        hbox.Add((5,0), 0)
        myform['decim'] = form.static_float_field(
            parent=panel, sizer=hbox, label="Decim")

        hbox.Add((5,0), 1)
        myform['fs@usb'] = form.static_float_field(
            parent=panel, sizer=hbox, label="Fs@USB")

        hbox.Add((5,0), 1)
        myform['dbname'] = form.static_text_field(
            parent=panel, sizer=hbox)

        hbox.Add((5,0), 1)
        myform['baseband'] = form.static_float_field(
            parent=panel, sizer=hbox, label="Analog BB")

        hbox.Add((5,0), 1)
        myform['ddc'] = form.static_float_field(
            parent=panel, sizer=hbox, label="DDC")

        hbox.Add((5,0), 0)
        vbox.Add(hbox, 0, wx.EXPAND)

        
        
    def set_freq(self, target_freq):
        """
        Set the center frequency we're interested in.

        @param target_freq: frequency in Hz
        @rypte: bool

        Tuning is a two step process.  First we ask the front-end to
        tune as close to the desired frequency as it can.  Then we use
        the result of that operation and our target_frequency to
        determine the value for the digital down converter.
        """
        r = usrp.tune(self.u, 0, self.subdev, target_freq)
        
        if r:
            self.myform['freq'].set_value(target_freq)     # update displayed value
            if self.show_debug_info:
                self.myform['baseband'].set_value(r.baseband_freq)
                self.myform['ddc'].set_value(r.dxc_freq)
            return True

        return False

    def set_gain(self, gain):
        self.myform['gain'].set_value(gain)     # update displayed value
        self.subdev.set_gain(gain)


def main ():
    app = stdgui.stdapp(app_flow_graph, "Demodulate", nstatus=1)
    app.MainLoop()

if __name__ == '__main__':
    main ()
