#!/usr/bin/env python


from gnuradio import gr, gru, eng_notation, optfir
from gnuradio import audio
from gnuradio import usrp
from gnuradio import blks
from gnuradio.eng_option import eng_option
from gnuradio.wxgui import slider, powermate
from gnuradio.wxgui import stdgui, fftsink, form
#from gnuradio.wxgui import *
from optparse import OptionParser
import usrp_dbid
import sys
import math
from math import sin, cos
import wx

def pick_subdevice(u):
    """
    The user didn't specify a subdevice on the command line.
    Try for one of these, in order: TV_RX, BASIC_RX, whatever is on side A.

    @return a subdev_spec
    """
    return usrp.pick_subdev(u, (usrp_dbid.TV_RX,
                                usrp_dbid.TV_RX_REV_2,
                                usrp_dbid.BASIC_RX))

class parallel_graph_sink_f(gr.hier_block, fftsink.fft_sink_base):
    def __init__(self, fg, parent, baseband_freq=0,
                 y_per_div=10, ref_level=0, sample_rate=1, block_size=512,
                 display_rate=15, average=False, avg_alpha=None, title='',
                 size=fftsink.default_fftsink_size, peak_hold=False,
                 log_type=None,log_offset=0):

        fftsink.fft_sink_base.__init__(self, input_is_real=False, baseband_freq=baseband_freq,
                               y_per_div=y_per_div, ref_level=ref_level,
                               sample_rate=sample_rate, fft_size=block_size,
                               fft_rate=display_rate,
                               average=average, avg_alpha=avg_alpha, title=title,
                               peak_hold=peak_hold)

        one_in_n = gr.keep_one_in_n(gr.sizeof_float * block_size,
                                    max(1, int(sample_rate/block_size/display_rate)))
        self.avg = gr.single_pole_iir_filter_ff(1.0, block_size)
        sink = gr.message_sink(gr.sizeof_float * block_size, self.msgq, True)
        if not (log_type is None):
          log = gr.nlog10_ff(20, block_size, log_offset)
        if log_type is None:
          fg.connect(one_in_n, self.avg, sink)
        elif log_type=='PRE_AVG':
          fg.connect(one_in_n,log, self.avg, sink)
        elif log_type=='POST_AVG':
          fg.connect(one_in_n, self.avg,log, sink)
        else:
          print 'warning log=',log,' not understood.'
          print "use 'PRE_AVG', 'POST_AVG' or None"
        gr.hier_block.__init__(self, fg, one_in_n, sink)
        self.win = fftsink.fft_window(self, parent, size=size)
        self.set_average(self.average)

class correlator_c(gr.hier_block):
    def __init__(self, fg, sample_rate=1, fft_size=512,output_type='COMPLEX',
                 fft_rate=15, pre_abs=False,subset_result=False): #01.0e-12):
        di = gr.deinterleave(gr.sizeof_gr_complex)
        s2p_a = gr.serial_to_parallel(gr.sizeof_gr_complex, fft_size)
        s2p_b = gr.serial_to_parallel(gr.sizeof_gr_complex, fft_size)
        s2p3 = gr.serial_to_parallel(gr.sizeof_gr_complex, fft_size)
        self.decimation=max(1, int(sample_rate/fft_size/fft_rate))
        one_in_n_a = gr.keep_one_in_n(gr.sizeof_gr_complex * fft_size,
                                    self.decimation)
        one_in_n_b = gr.keep_one_in_n(gr.sizeof_gr_complex * fft_size,
                                    self.decimation)
        if subset_result:
          fft_size_result=int(fft_size/4)
        else:
          fft_size_result=fft_size
        mywindow = fftsink.window.blackmanharris(fft_size)
        if pre_abs:
          c2mag1_a = gr.complex_to_mag(fft_size)
          c2mag1_b = gr.complex_to_mag(fft_size)
          fft_a = gr.fft_vfc(fft_size, True, mywindow)
          fft_b = gr.fft_vfc(fft_size, True, mywindow)
        else:
          fft_a = gr.fft_vcc(fft_size, True, mywindow)
          fft_b = gr.fft_vcc(fft_size, True, mywindow)
        self.power = 0
        for tap in mywindow:
            self.power += tap*tap

        p2s_a = gr.parallel_to_serial(gr.sizeof_gr_complex, fft_size)
        p2s_b = gr.parallel_to_serial(gr.sizeof_gr_complex, fft_size)
        conj=gr.conjugate_cc()
        mult=gr.multiply_cc()
        ifft=gr.fft_vcc(fft_size, False, mywindow)
        if output_type=='REAL':
          c2mag2 = gr.complex_to_real(fft_size_result) #
        elif output_type=='MAG':
          c2mag2=gr.complex_to_mag(fft_size_result)
        else: #output_type='COMPLEX'
          c2mag2=gr.add_const_cc(0.0) #output complex values, use dummy placeholder

        #self.avg_cc = gr.single_pole_iir_filter_cc(alpha2, fft_size_result)
        fg.connect((di,0),s2p_a,one_in_n_a)
        fg.connect((di,1),s2p_b,one_in_n_b)
        if pre_abs:
          fg.connect(one_in_n_a,c2mag1_a,fft_a,p2s_a)
          fg.connect(one_in_n_b,c2mag1_b,fft_b,p2s_b)
        else:
          fg.connect(one_in_n_a,fft_a,p2s_a)
          fg.connect(one_in_n_b,fft_b,p2s_b)
        dummy=gr.add_const_cc(0.0)
        fg.connect(p2s_a,conj)
        fg.connect(p2s_b,dummy)
        fg.connect(dummy,(mult,0))
        fg.connect(conj,(mult,1))
        fg.connect(mult,s2p3,ifft)
        if subset_result:
          print 'SUBSET'
          p2s4=gr.parallel_to_serial(gr.sizeof_gr_complex*(fft_size_result), 4)
          di2=gr.deinterleave(gr.sizeof_gr_complex*fft_size_result)
          nullsink0=gr.null_sink(gr.sizeof_gr_complex*fft_size_result)
          nullsink1=gr.null_sink(gr.sizeof_gr_complex*fft_size_result)
          #nullsink2=gr.null_sink(gr.sizeof_gr_complex*(fft_size/4))
          nullsink3=gr.null_sink(gr.sizeof_gr_complex*fft_size_result)
          fg.connect(ifft,p2s4,di2)
          fg.connect((di2,0),nullsink0)
          fg.connect((di2,1),nullsink1)
          ifft_result=(di2,2)
          #fg.connect((di2,2),c2mag2)
          #fg.connect((di2,2),nullsink2)
          fg.connect((di2,3),nullsink3)
        else:
          ifft_result=ifft
        if output_type=='COMPLEX':
          sink=ifft_result
        else:
          fg.connect(ifft_result,c2mag2)
          sink=c2mag2

        gr.hier_block.__init__(self, fg, di, sink)

class correlate_sink_c2(gr.hier_block):
    def __init__(self, fg, parent, baseband_freq=0,
                 y_per_div=10, ref_level=50, sample_rate=1, fft_size=512,
                 fft_rate=15, average=False, avg_alpha=None, title='',
                 size=fftsink.default_fftsink_size, peak_hold=False):

        corr=correlator_c(fg, sample_rate=sample_rate,fft_size=fft_size,output_type='MAG',
                 fft_rate=fft_rate, pre_abs=False,subset_result=False)
        log_offset= -20*math.log10(fft_size)-10*math.log10(corr.power/fft_size)
        
        sink=parallel_graph_sink_f( fg=fg, parent=parent, baseband_freq=0,
                 y_per_div=y_per_div, ref_level=ref_level,
                 sample_rate=sample_rate,block_size=fft_size,
                 display_rate=fft_rate*10000, average=average, avg_alpha=avg_alpha, title=title,
                 size=size, peak_hold=peak_hold,
                 log_type='POST_AVG',log_offset=log_offset)
        self.win=sink.win
        fg.connect(corr,sink)
        gr.hier_block.__init__(self, fg, corr, sink)

class correlate_sink_c3(gr.hier_block):
    def __init__(self, fg, parent, baseband_freq=0,
                 y_per_div=10, ref_level=50, sample_rate=1, fft_size=512,
                 fft_rate=15, average=False, avg_alpha=None, title='',
                 size=fftsink.default_fftsink_size, peak_hold=False):

        corr0=correlator_c(fg, sample_rate=sample_rate,fft_size=fft_size,output_type='COMPLEX',
                 fft_rate=fft_rate, pre_abs=False,subset_result=False)
        corr1=correlator_c(fg, sample_rate=sample_rate,fft_size=fft_size,output_type='COMPLEX',
                 fft_rate=fft_rate, pre_abs=False,subset_result=False)
        log_offset= -20*math.log10(fft_size)-10*math.log10(corr0.power/fft_size)
        
        sink=parallel_graph_sink_f( fg=fg, parent=parent, baseband_freq=0,
                 y_per_div=y_per_div, ref_level=ref_level,
                 sample_rate=sample_rate,block_size=fft_size,
                 display_rate=fft_rate*100000, average=average, avg_alpha=avg_alpha, title=title,
                 size=size, peak_hold=peak_hold,
                 log_type=None,log_offset=log_offset)
        self.win=sink.win
        fft_size_result=fft_size
        di = gr.deinterleave(gr.sizeof_gr_complex)
        interleaver0= gr.interleave(gr.sizeof_gr_complex)
        fg.connect((di,0),(interleaver0,0))
        fg.connect((di,0),(interleaver0,1))
        interleaver1= gr.interleave(gr.sizeof_gr_complex)
        fg.connect((di,1),(interleaver1,0))
        fg.connect((di,1),(interleaver1,1))
        fg.connect(interleaver0,corr0)
        fg.connect(interleaver1,corr1)
        postdiff=gr.sub_cc()
        postp2s0=gr.parallel_to_serial(gr.sizeof_gr_complex,fft_size_result)
        postp2s1=gr.parallel_to_serial(gr.sizeof_gr_complex,fft_size_result)
        posts2p=gr.serial_to_parallel(gr.sizeof_gr_complex,fft_size_result)
        fg.connect(corr0,postp2s0,(postdiff,0))
        fg.connect(corr1,postp2s1,(postdiff,1))
        c2mag = gr.complex_to_mag(fft_size_result)
        #c2mag = gr.complex_to_real(fft_size_result)
        fg.connect(postdiff,posts2p,c2mag,sink)
        gr.hier_block.__init__(self, fg, di, sink)


class autocorrelate_sink_c(gr.hier_block, fftsink.fft_sink_base):
    def __init__(self, fg, parent, baseband_freq=0,
                 y_per_div=10, ref_level=50, sample_rate=1, fft_size=512,
                 fft_rate=15, average=False, avg_alpha=None, title='',
                 size=fftsink.default_fftsink_size, peak_hold=False):

        fftsink.fft_sink_base.__init__(self, input_is_real=False, baseband_freq=baseband_freq,
                               y_per_div=y_per_div, ref_level=ref_level,
                               sample_rate=sample_rate, fft_size=fft_size,
                               fft_rate=fft_rate,
                               average=average, avg_alpha=avg_alpha, title=title,
                               peak_hold=peak_hold)

        s2p = gr.serial_to_parallel(gr.sizeof_gr_complex, fft_size)
        s2p2 = gr.serial_to_parallel(gr.sizeof_gr_complex, fft_size)
        one_in_n = gr.keep_one_in_n(gr.sizeof_gr_complex * fft_size,
                                    max(1, int(sample_rate/fft_size/fft_rate)))
        pre_abs=False
        #use_window=True
        mywindow = fftsink.window.blackmanharris(fft_size)
        if pre_abs:
          c2mag1 = gr.complex_to_mag(fft_size)
          fft = gr.fft_vfc(fft_size, True, mywindow)
        else:
          fft = gr.fft_vcc(fft_size, True, mywindow)
        power = 0
        for tap in mywindow:
            power += tap*tap

        p2s = gr.parallel_to_serial(gr.sizeof_gr_complex, fft_size)
        conj=gr.conjugate_cc()
        mult=gr.multiply_cc()
        ifft=gr.fft_vcc(fft_size, False, mywindow)
        #c2mag2 = gr.complex_to_mag(fft_size)
        c2mag2 = gr.complex_to_arg(fft_size)
        self.avg = gr.single_pole_iir_filter_ff(1.0, fft_size)
        #log = gr.nlog10_ff(20, fft_size, -20*math.log10(fft_size))
        log = gr.nlog10_ff(20, fft_size, -20*math.log10(fft_size)-10*math.log10(power/fft_size))
        sink = gr.message_sink(gr.sizeof_float * fft_size, self.msgq, True)

        fg.connect(s2p, one_in_n)
        if pre_abs:
          fg.connect(one_in_n,c2mag1,fft,p2s)
        else:
          fg.connect(one_in_n,fft,p2s)
        dummy=gr.add_const_cc(0.0)
        fg.connect(p2s,conj)
        fg.connect(p2s,dummy)
        fg.connect(dummy,(mult,0))
        fg.connect(conj,(mult,1))
        fg.connect(mult,s2p2,ifft)
        fg.connect(ifft, c2mag2, self.avg)
        post_log=True
        if post_log:
          fg.connect(self.avg, log, sink)
        else:
          fg.connect(self.avg, sink)
        gr.hier_block.__init__(self, fg, s2p, sink)

        self.win = fftsink.fft_window(self, parent, size=size)
        self.set_average(self.average)

class correlate_sink_c(gr.hier_block, fftsink.fft_sink_base):
    def __init__(self, fg, parent, baseband_freq=0,
                 y_per_div=10, ref_level=50, sample_rate=1, fft_size=512,
                 fft_rate=15, average=False, avg_alpha=None, title='',
                 size=fftsink.default_fftsink_size, peak_hold=False,
                 pre_abs=False,post_log=False,subset_result=False,alpha2=0.01/2,alpha3=0.01/4): #01.0e-12):

        fftsink.fft_sink_base.__init__(self, input_is_real=False, baseband_freq=baseband_freq,
                               y_per_div=y_per_div, ref_level=ref_level,
                               sample_rate=sample_rate, fft_size=fft_size,
                               fft_rate=fft_rate,
                               average=average, avg_alpha=avg_alpha, title=title,
                               peak_hold=peak_hold)

        di = gr.deinterleave(gr.sizeof_gr_complex)
        s2p_a = gr.serial_to_parallel(gr.sizeof_gr_complex, fft_size)
        s2p_b = gr.serial_to_parallel(gr.sizeof_gr_complex, fft_size)
        s2p3 = gr.serial_to_parallel(gr.sizeof_gr_complex, fft_size)
        one_in_n_a = gr.keep_one_in_n(gr.sizeof_gr_complex * fft_size,
                                    max(1, int(sample_rate/fft_size/fft_rate)))
        one_in_n_b = gr.keep_one_in_n(gr.sizeof_gr_complex * fft_size,
                                    max(1, int(sample_rate/fft_size/fft_rate)))
        #subset_result=True
        #pre_abs=False
        print 'BLAAAAAA',alpha2
        if subset_result:
          fft_size_result=fft_size/4
        else:
          fft_size_result=fft_size
        mywindow = fftsink.window.blackmanharris(fft_size)
        if pre_abs:
          c2mag1_a = gr.complex_to_mag(fft_size)
          c2mag1_b = gr.complex_to_mag(fft_size)
          fft_a = gr.fft_vfc(fft_size, True, mywindow)
          fft_b = gr.fft_vfc(fft_size, True, mywindow)
        else:
          fft_a = gr.fft_vcc(fft_size, True, mywindow)
          fft_b = gr.fft_vcc(fft_size, True, mywindow)
        power = 0
        for tap in mywindow:
            power += tap*tap

        p2s_a = gr.parallel_to_serial(gr.sizeof_gr_complex, fft_size)
        p2s_b = gr.parallel_to_serial(gr.sizeof_gr_complex, fft_size)
        conj=gr.conjugate_cc()
        mult=gr.multiply_cc()
        ifft=gr.fft_vcc(fft_size, False, mywindow)
        c2mag2 = gr.complex_to_real(fft_size_result) #
        #c2mag2=gr.complex_to_mag(fft_size_result)
        self.avg = gr.single_pole_iir_filter_ff(avg_alpha, fft_size_result)
        self.avgx = gr.single_pole_iir_filter_ff(alpha2, fft_size_result)
        self.avgy = gr.single_pole_iir_filter_ff(alpha3, fft_size_result)
        self.postdiff=gr.sub_ff()
        self.avg_cc = gr.single_pole_iir_filter_cc(alpha2, fft_size_result)
        #log = gr.nlog10_ff(20, fft_size, -20*math.log10(fft_size))
        log = gr.nlog10_ff(20, fft_size_result, -20*math.log10(fft_size_result)-10*math.log10(power/fft_size_result))

        sink = gr.message_sink(gr.sizeof_float * fft_size_result, self.msgq, True)

        fg.connect((di,0),s2p_a,one_in_n_a)
        fg.connect((di,1),s2p_b,one_in_n_b)
        if pre_abs:
          fg.connect(one_in_n_a,c2mag1_a,fft_a,p2s_a)
          fg.connect(one_in_n_b,c2mag1_b,fft_b,p2s_b)
        else:
          fg.connect(one_in_n_a,fft_a,p2s_a)
          fg.connect(one_in_n_b,fft_b,p2s_b)
        dummy=gr.add_const_cc(0.0)
        fg.connect(p2s_a,conj)
        fg.connect(p2s_b,dummy)
        fg.connect(dummy,(mult,0))
        fg.connect(conj,(mult,1))
        fg.connect(mult,s2p3,ifft)
        if subset_result:
          print 'SUBSET'
          #c2mag2_alt = gr.complex_to_mag(int(fft_size/4))
          p2s4=gr.parallel_to_serial(gr.sizeof_gr_complex*(fft_size/4), 4)
          #p2s4=gr.parallel_to_serial(gr.sizeof_gr_complex, fft_size)
          #s2p4=gr.serial_to_parallel(gr.sizeof_gr_complex, fft_size/4)
          di2=gr.deinterleave(gr.sizeof_gr_complex*int(fft_size/4))
          nullsink0=gr.null_sink(gr.sizeof_gr_complex*(fft_size/4))
          nullsink1=gr.null_sink(gr.sizeof_gr_complex*(fft_size/4))
          #nullsink2=gr.null_sink(gr.sizeof_gr_complex*(fft_size/4))
          nullsink3=gr.null_sink(gr.sizeof_gr_complex*(fft_size/4))
          fg.connect(ifft,p2s4,di2)
          #fg.connect(p2s4,s2p4)
          #fg.connect(s2p4,di2)
          fg.connect((di2,0),nullsink0)
          fg.connect((di2,1),nullsink1)
          fg.connect((di2,2),c2mag2)
          #fg.connect((di2,2),nullsink2)
          fg.connect((di2,3),nullsink3)

          #fg.connect(c2mag2_alt,self.avg)
          fg.connect( c2mag2, self.avg)
        else:
          fg.connect(ifft, c2mag2)#, self.avg)
          #fg.connect(ifft, self.avg_cc,c2mag2)
        #post_log=True

        #fg.connect(c2mag2,self.avgx,log, sink)
        postp2sx=gr.parallel_to_serial(gr.sizeof_float,fft_size_result)
        postp2sy=gr.parallel_to_serial(gr.sizeof_float,fft_size_result)
        posts2p=gr.serial_to_parallel(gr.sizeof_float,fft_size_result)
        fg.connect(c2mag2,self.avgx,postp2sx,(self.postdiff,0))
        fg.connect(c2mag2,self.avgy,postp2sy,(self.postdiff,1))
        #one_in_n_x = gr.keep_one_in_n(gr.sizeof_float * fft_size_result,
        #                            max(1, int(sample_rate/fft_size_result/fft_rate)))
        #one_in_n_y = gr.keep_one_in_n(gr.sizeof_float * fft_size_result,
        #                            max(1, int(sample_rate/fft_size_result/fft_rate)))
        if post_log:
          fg.connect(self.postdiff,posts2p,log, self.avg, sink)
          #fg.connect(c2mag2,log,sink)
        else:
          fg.connect(self.postdiff,posts2p, self.avg, sink)
          #fg.connect(c2mag2,self.avgx, sink)
          #fg.connect(c2mag2,sink)
        gr.hier_block.__init__(self, fg, di, sink)

        self.win = fftsink.fft_window(self, parent, size=size)
        self.set_average(self.average)

class siggen_wfm_source_c(gr.hier_block):
    def __init__(self, fg, sample_rate=64e6,
                 rf_carrier_freq=107.9e6, rf_amp=1.0,
                 audio_tone_freq=1.0e3, audio_tone_amp=0.5, 
                 audio_noise_amp=1.0e-6, if_noise_amp=1.0e-6, rf_noise_amp=1.0e-6): 
        rf_rate = sample_rate #float(int(sample_rate))                   # 64 MS/s
        rf_interp = max(1,int(rf_rate/3200e3)) #20
        print 'rf_interp',rf_interp
        if_rate = rf_rate /rf_interp          # 3200 kS/s
        if_chanfilt_interp = max(1,int(if_rate/320e3)) #10
        mod_rate =if_rate / if_chanfilt_interp # 320 kS/s
        audio_interp = max(1,int(mod_rate/40e3)) # 10
        audio_rate = mod_rate / audio_interp    # 32 kHz


        #audio_rate=32e3
        #audio_interp=max(10,min(1,int(rf_rate/audio_rate)))
        #if_chanfilt_interp=max(10,min(1,int(rf_rate/audio_rate)))
        print 'ar',audio_rate,'mr',mod_rate,'ir',if_rate,'rr',rf_rate
        if audio_tone_amp>1.0: 
           print 'error, audio_tone_amp must be < 1.0, it is ',audio_tone_amp
        audio_tone_src=gr.sig_source_f(audio_rate, gr.GR_SIN_WAVE, audio_tone_freq,audio_tone_amp,0.0 ) #amp must be <=1.0
        audio_noise_src=gr.noise_source_f  	(gr.GR_GAUSSIAN,audio_noise_amp,12345678	)
        #noise types: GR_UNIFORM, GR_GAUSSIAN, GR_LAPLACIAN, GR_IMPULSE
        audio_add=gr.add_ff() 

        fm_modulator=blks.wfm_tx(fg, audio_rate, mod_rate)
        if_chan_filt_coeffs = optfir.low_pass (1,           # gain
                                            if_rate,   # sampling rate
                                            80e3,        # passband cutoff
                                            115e3,       # stopband cutoff
                                            0.1,         # passband ripple
                                            60)          # stopband attenuation
        print 'siggen_fm_source_c::len(if_chan_filt_coeffs)', len(if_chan_filt_coeffs)
        if_chan_filt = gr.interp_fir_filter_ccf(if_chanfilt_interp, if_chan_filt_coeffs)
        if_noise_src=gr.noise_source_c(gr.GR_GAUSSIAN,if_noise_amp,87654321	)
        if_add=gr.add_cc() 
        rf_chan_filt_coeffs = optfir.low_pass (1,           # gain
                                            rf_rate,   # sampling rate
                                            if_rate/4, #0.8 Mhz       # passband cutoff
                                            if_rate/2, #1.6 Mhz      # stopband cutoff
                                            0.1,         # passband ripple
                                            60)          # stopband attenuation  
        print 'siggen_fm_source_c::len(rf_chan_filt_coeffs)', len(rf_chan_filt_coeffs)      
        rf_interp=gr.interp_fir_filter_ccf(rf_interp, rf_chan_filt_coeffs)
        rf_mult=gr.multiply_cc()
        rf_carrier=gr.sig_source_c(rf_rate, gr.GR_SIN_WAVE, rf_carrier_freq,rf_amp,0.0 ) 
        rf_noise_src=gr.noise_source_c(gr.GR_GAUSSIAN,rf_noise_amp,31415926	)
        rf_add=gr.add_cc() 
        if audio_noise_amp!=0:
          fg.connect(audio_tone_src,(audio_add,0))
          fg.connect(audio_noise_src,(audio_add,1))
          audio=audio_add
        else:
          audio=audio_tone_src
        fg.connect(audio,fm_modulator,if_chan_filt)
        if if_noise_amp!=0:
          fg.connect(if_chan_filt,(if_add,0))
          fg.connect(if_noise_src,(if_add,1))
          if_block=if_add
        else:
          if_block=if_chan_filt
        fg.connect(if_block,rf_interp,(rf_mult,0))
        fg.connect(rf_carrier,(rf_mult,1))
        if rf_noise_amp!=0:
          fg.connect(rf_mult,(rf_add,0))
          fg.connect(rf_noise_src,(rf_add,1))
          rf=rf_add
          print 'RF noise'
        else:
          rf=rf_mult
        gr.hier_block.__init__(self, fg, None, rf)

class fake_usrp_source_c(gr.hier_block):
    def __init__(self,fg, which=0, decim_rate=64, nchan=1, mux=0x32103210, mode=0,target_freq=0,do_filter=True,do_avg=False):
        self.adc_rate=long(64e6)
        factor=100 #200,100,40,20,10,1
        self.sample_rate=self.adc_rate/factor
        self.decim_rate= decim_rate/(factor) #int(max(1,int(decim_rate/(self.adc_rate/self.sample_rate))))
        print 'sr',self.adc_rate,self.sample_rate,self.decim_rate
        self.nchan=nchan
        self.mux=mux
        self.target_freq=target_freq
        self.do_filter=do_filter
        self.do_avg=do_avg
        #self.fm_src=siggen_wfm_source_c(fg=fg,sample_rate=self.adc_rate)
        self.fm_src=siggen_wfm_source_c(fg=fg,sample_rate=self.sample_rate,
                 rf_carrier_freq=target_freq,audio_noise_amp=0.1, if_noise_amp=0, rf_noise_amp=0.1) #0.25)
        self.fg=fg
        self.tune(None,None,target_freq)

    def tune(self, chan, subdev, target_freq):
        self.target_freq=target_freq
        self.connect_blocks()

    def connect_blocks(self):
        if self.do_filter:
          self.rf_chan_filt_coeffs = optfir.low_pass (1,           # gain
                                            self.sample_rate,   # sampling rate
                                            self.sample_rate/(self.decim_rate*2),        # passband cutoff
                                            self.sample_rate/(self.decim_rate),       # stopband cutoff
                                            0.1,         # passband ripple
                                            60)          # stopband attenuation
          self.dec=gr.freq_xlating_fir_filter_ccf (self.decim_rate, self.rf_chan_filt_coeffs , self.target_freq, self.sample_rate)
        else:
          if self.do_avg:
            avg.coeffs=[]
            for tap in range (1,decimation):
              avg.coeffs.append(1.0)
            self.dec=gr.fir_filter_ccf(self.decim_rate,avg_coeffs,self.sample_rate)
          else:
            self.dec=gr.keep_one_in_n(gr.sizeof_gr_complex,self.decim_rate)
        self.fg.connect(self.fm_src,self.dec)
        gr.hier_block.__init__(self, self.fg, None, self.dec)

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

        parser=OptionParser(option_class=eng_option)
        parser.add_option("-R", "--rx-subdev-spec-directional", type="subdev", default=(0,0),
                          help="select USRP Rx side A or B (default=A)")
        parser.add_option("-S", "--rx-subdev-spec-omni", type="subdev", default=(1,0),
                          help="select USRP Rx side A or B (default=B)")
        parser.add_option("-f", "--freq", type="eng_float", default=None, #100.1e6,
                          help="set frequency to FREQ", metavar="FREQ")
        parser.add_option("-g", "--gain", type="eng_float", default=None,
                          help="set gain in dB (default is midpoint)")
        parser.add_option("-V", "--volume", type="eng_float", default=None,
                          help="set volume (default is midpoint)")
        parser.add_option("-O", "--audio-output", type="string", default="",
                          help="pcm device name.  E.g., hw:0,0 or surround51 or /dev/dsp")

        (options, args) = parser.parse_args()
        if len(args) != 0:
            parser.print_help()
            sys.exit(1)
        
        self.frame = frame
        self.panel = panel
        
        self.vol = 0
        self.state = "FREQ"
        self.freq = 0

        # build graph
        usrp_decim = 200        
        self.u = usrp.source_c(0,usrp_decim,2)                    # usrp is data source
        from_file=True
        to_file=False
        file_time=100e-3
        filename="fake_usrp_wfm_100msec_1.dat"
        if from_file:
          self.fake_u=gr.file_source(gr.sizeof_gr_complex,filename,True)
        else:
          self.fake_u=fake_usrp_source_c(self,0, usrp_decim, 1, 0x32103210, 0,options.freq,do_filter=False,do_avg=False)
        nullsink_fake=gr.null_sink(gr.sizeof_gr_complex)

        adc_rate = self.u.adc_rate()                # 64 MS/s

        self.u.set_decim_rate(usrp_decim)
        usrp_rate = adc_rate / usrp_decim           # 320 kS/s
        if to_file:
          saver=gr.file_sink(gr.sizeof_gr_complex,filename)
          sh=gr.skiphead(gr.sizeof_gr_complex,1024)
          hd=gr.head(gr.sizeof_gr_complex,int(float(usrp_rate)*float(file_time))) # save ten seconds
          #self.connect(self.fake_u,nullsink_fake)
          self.connect(self.fake_u,sh,hd,saver) #nullsink_fake) #keep it happy when not used
        chanfilt_decim = 1
        demod_rate = usrp_rate / chanfilt_decim
        audio_decimation = 10
        audio_rate = demod_rate / audio_decimation  # 32 kHz
        print 'audio_rate',audio_rate

        if options.rx_subdev_spec_directional is None:
            options.rx_subdev_spec_directional = pick_subdevice(self.u)

        mux_d=usrp.determine_rx_mux_value(self.u, options.rx_subdev_spec_directional)
        print "mux_d",mux_d
        mux_o=usrp.determine_rx_mux_value(self.u, options.rx_subdev_spec_omni)<<8
        print "mux_o",mux_o
        mux=mux_d | mux_o
        self.u.set_mux(mux)
        self.subdev_d = usrp.selected_subdev(self.u, options.rx_subdev_spec_directional)
        print "Using directional RX d'board %s" % (self.subdev_d.side_and_name(),)
        self.subdev_o = usrp.selected_subdev(self.u, options.rx_subdev_spec_omni)
        print "Using omni RX d'board %s" % (self.subdev_o.side_and_name(),)

        self.skiphead=gr.skiphead(gr.sizeof_gr_complex,1024)#2*512*8) #size_t sizeof_stream_item, int nitems
        # deinterleave two channels from FPGA
        self.di = gr.deinterleave(gr.sizeof_gr_complex)
        self.agc_d=gr.agc_cc(1e-4,1.0,1.0) #defaults (float rate = 1e-4, float reference = 1.0, float gain = 1.0)
        self.agc_o=gr.agc_cc(1e-4,1.0,1.0)
        self.phase_shifter=gr.multiply_const_cc(complex(cos(0),sin(0)))
        self.phase_shifter2=gr.multiply_const_cc(complex(cos(0),sin(0)))
        delay_taps=[]
        for x in range(0,7):
          delay_taps.append(0.0)
        #(0.0,0.0,0.0,0.0,0.0,0.0,0.0)
        self.delay_value=0
        delay_taps[self.delay_value+3]=1.0
        self.delay_d=gr.fir_filter_ccf  	(1,delay_taps)
	self.delay_o=gr.fir_filter_ccf  	(1,delay_taps)
        self.diff=gr.sub_cc()
        #chan_filt_coeffs = optfir.low_pass (1,           # gain
        #                                    usrp_rate,   # sampling rate
        #                                    80e3,        # passband cutoff
        #                                    115e3,       # stopband cutoff
        #                                    0.1,         # passband ripple
        #                                    60)          # stopband attenuation
        chan_filt_coeffs = optfir.low_pass (1,           # gain
                                            usrp_rate,   # sampling rate
                                            80e3,        # passband cutoff
                                            115e3,       # stopband cutoff
                                            0.1,         # passband ripple
                                            60)          # stopband attenuation
        #print len(chan_filt_coeffs)
        self.chan_filt_d = gr.fir_filter_ccf (chanfilt_decim, chan_filt_coeffs)
        self.chan_filt_o = gr.fir_filter_ccf (chanfilt_decim, chan_filt_coeffs)
        self.guts = blks.wfm_rcv (self, demod_rate, audio_decimation)

        self.volume_control = gr.multiply_const_ff(self.vol)

        # sound card as final sink
        audio_sink = audio.sink (int (audio_rate), options.audio_output)

        
        # now wire it all together

        self.connect (self.u, self.skiphead,self.di)
        #self.connect((self.di,0), self.phase_shifter,self.delay_d,self.agc_d, self.chan_filt_d, self.guts, self.volume_control, audio_sink)
        #nullsink_di1=gr.null_sink(gr.sizeof_gr_complex)
        #self.connect((self.di,1), self.phase_shifter2,self.delay_o,self.agc_o, self.chan_filt_o,nullsink_di1)
        self.connect((self.di,0), self.agc_d) #, self.chan_filt_d)#, self.guts, self.volume_control, audio_sink)
        nullx=gr.null_sink(gr.sizeof_gr_complex)
        self.connect(self.agc_d,nullx)
        self.connect(self.fake_u, self.chan_filt_d, self.guts, self.volume_control, audio_sink)
        nullsink_di1=gr.null_sink(gr.sizeof_gr_complex)
        self.connect((self.di,1), self.agc_o, self.chan_filt_o,nullsink_di1)

        #self.connect((self.di,1),nullsink_di1)
        self._build_gui(vbox, usrp_rate, demod_rate, audio_rate, options.freq)

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

        if options.volume is None:
            g = self.volume_range()
            options.volume = float(g[0]+g[1])/2

        if options.freq is None:
            options.freq=940.4 #gsm900 channel 27 downlink (vodafone)

        if abs(options.freq) < 1e6:
            options.freq *= 1e6            


        # set initial values

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


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


    def _build_gui(self, vbox, usrp_rate, demod_rate, audio_rate,freq_center):

        def _form_set_freq(kv):
            return self.set_freq(kv['freq'])



        
        # control area form at bottom
        self.myform = myform = form.form()

        hbox = wx.BoxSizer(wx.HORIZONTAL)
        hbox.Add((5,0), 0)
        myform['freq'] = form.float_field(
            parent=self.panel, sizer=hbox, label="Freq", weight=1,
            callback=myform.check_input_and_call(_form_set_freq, self._set_status_msg))

        hbox.Add((5,0), 0)
        freq_low_standard=939.2e6 #gsm900 channel 21 downlink            87.9e6
        freq_high_standard=948.0e6#gsm900 channel 65 downlink            108.1e6
#gsm frequencies The Netherlands
#ProRail                                        KPN Mobile                                      Vodafone
#20 Kanalen GSM-R                               62 kanalen GSM900 en 88 kanalen GSM1800 	57 Kanalen GSM900 en 26 Kanalen GSM1800
#Kanalen 	Uplink	 	Downlink 	Kanalen 	Uplink	 	Downlink 	Kanalen 	Uplink	 	Downlink
#n/a(20) 	876,2-880,0 	921,2-925,0 	1-20(20) 	890,2-894,0 	935,2-939,0 	21-65(45) 	894,2-903,0 	939,2-948,0
#                                               66-106(42) 	881,0-882,2 	926,0-925,2 	975-978(4) 	880,2-880,8 	925,2-925,8
#                                               108-119(12) 	911,6-913,8 	956,6-958,8 	537-549(13) 	1715,2-1717,6 	1810,2-1812,6
#                                               525 -536(12) 	1712,8-1715,0 	1807,2-1810,0 	512-524(13) 	1710,2-1805,2 	1805,2-1807,6
#                                               562-574(13) 	1720,2-1722,6 	1818,2-1817,2 	837-849(13) 	1775,2-1777,6 	1870,2-1872,6
#                                               587-611(25) 	1725,4-1730,0 	1820,4-1825,0 	637-649(13) 	1735,2-1830,6 	1830,2-1832,6
#                                               812-836(25) 	1770,2-1775,0 	1865,2-1870,0 			
#Telfort                                        Orange                                          T-Mobile
#25 Kanalen E-GSM en 87 Kanalen GSM1800 	25 Kanalen E-GSM en 75 Kanalen GSM1800          84 Kanalen GSM1800
#Kanalen 	Uplink	 	Downlink 	Kanalen 	Uplink	 	Downlink 	Kanalen 	Uplink	 	Downlink
#979-985(7) 	881,0-882,2 	926,0-925,2 	975-978(4) 	880,2-880,8 	925,2-925,8 	537-549(13) 	1715,2-1717,6 	1810,2-1812,6
#1007-1024(18) 	886,6-890,0 	931,6-935,0 	986-1006(21) 	882,4-886,4 	927,4-931,4 	575-586(12) 	1722,8-1725,0 	1817,8-1820,0
#550-561(12) 	1717,8-1720,0 	1812,8-1815,0 	662-736(75) 	1740,2-1755,0 	1835,2-1850,0 	612-636(25) 	1730,2-1735,0 	1825,2-1830,0
#737-811(75) 	1755,2-1770,0 	1850,2-1865,0                                                   650-661(12) 	1737,8-1740,0 	1832,8-1835,0
#                                                                                               850-871(22) 	1777,8-1782,0 	1872,8-1877,0
# alle frequenties in MHz
        freq_center_standard=(freq_low_standard+freq_high_standard)/2.0
        if freq_center is None:
          freq_center=freq_center_standard
        if abs(freq_center) < 1e6:
            freq_center *= 1e6   
        freq_low=freq_low_standard + freq_center - freq_center_standard
        freq_high=freq_high_standard + freq_center - freq_center_standard
        myform['freq_slider'] = \
            form.quantized_slider_field(parent=self.panel, sizer=hbox, weight=3,
                                        range=(freq_low, freq_high, 0.1e6),
                                        callback=self.set_freq)
        hbox.Add((5,0), 0)
        vbox.Add(hbox, 0, wx.EXPAND)

        hbox = wx.BoxSizer(wx.HORIZONTAL)
        hbox.Add((5,0), 0)

        myform['volume'] = \
            form.quantized_slider_field(parent=self.panel, sizer=hbox, label="Volume",
                                        weight=3, range=self.volume_range(),
                                        callback=self.set_vol)
        hbox.Add((5,0), 1)

        myform['gain'] = \
            form.quantized_slider_field(parent=self.panel, sizer=hbox, label="Gain",
                                        weight=3, range=self.subdev_d.gain_range(),
                                        callback=self.set_gain)
        hbox.Add((5,0), 2)
        myform['phase'] = \
            form.quantized_slider_field(parent=self.panel, sizer=hbox, label="Phase",
                                        weight=3, range=(-180, 180, 3),
                                        callback=self.set_phase)
        hbox.Add((5,0), 3)
        myform['delay'] = \
            form.quantized_slider_field(parent=self.panel, sizer=hbox, label="Delay",
                                        weight=3, range=(-3, 3, 1),
                                        callback=self.set_delay)
        hbox.Add((5,0), 0)
        vbox.Add(hbox, 0, wx.EXPAND)
        display_rate=5
        alpha=2.0/display_rate
        if 0:
            self.src_fft_diff = fftsink.fft_sink_c (self, self.panel, title="Data from USRP d",
                                               fft_size=512, sample_rate=usrp_rate,fft_rate=display_rate,avg_alpha=alpha)
            self.connect (self.chan_filt_d, self.src_fft_diff)
            vbox.Add (self.src_fft_diff.win, 4, wx.EXPAND)
        if 1:
            self.src_fft_d = fftsink.fft_sink_c (self, self.panel, title="Data from fake_usrp",
                                               fft_size=512, sample_rate=usrp_rate,fft_rate=display_rate*1,avg_alpha=alpha)
            self.connect (self.fake_u, self.src_fft_d)
            vbox.Add (self.src_fft_d.win, 4, wx.EXPAND)
        if 0:
            self.src_fft_d = autocorrelate_sink_c (self, self.panel, title="autocorrelate d",
                                               fft_size=512, sample_rate=usrp_rate,fft_rate=display_rate,avg_alpha=alpha)
            self.connect (self.chan_filt_d, self.src_fft_d)
            vbox.Add (self.src_fft_d.win, 4, wx.EXPAND)
        if 0:
            self.src_fft_d = correlate_sink_c(self, self.panel, title="auto correlate d ",
                                               fft_size=512, sample_rate=usrp_rate,fft_rate=display_rate,avg_alpha=alpha/4)
            self.interleaver=gr.interleave(gr.sizeof_gr_complex)
            self.connect(self.chan_filt_d,(self.interleaver,0))
            self.connect(self.chan_filt_d,(self.interleaver,1))
            self.connect (self.interleaver, self.src_fft_d)
            vbox.Add (self.src_fft_d.win, 4, wx.EXPAND)
        #self.connect ((self.di,0),self.agc_d)
        #self.connect ((self.di,1),self.agc_o)
        if 0:
            x=1.0
            self.src_fft_d = correlate_sink_c3 (self, self.panel, title="correlate d and o c3",
                                               fft_size=int(1024*x), sample_rate=usrp_rate,fft_rate=display_rate/x,avg_alpha=alpha/5)
            self.interleaver=gr.interleave(gr.sizeof_gr_complex)
            self.connect(self.chan_filt_d,(self.interleaver,0))
            self.connect(self.chan_filt_o,(self.interleaver,1))
            self.connect (self.interleaver, self.src_fft_d)
            vbox.Add (self.src_fft_d.win, 4, wx.EXPAND)
        if 0:
            self.src_fft_o = fftsink.fft_sink_c (self, self.panel, title="Data from USRP o",
                                               fft_size=512, sample_rate=usrp_rate,fft_rate=display_rate)
            self.connect ((self.di,1), self.src_fft_o)
            vbox.Add (self.src_fft_o.win, 4, wx.EXPAND)
        if 0:
            self.src_fft_diff = fftsink.fft_sink_c (self, self.panel, title="Data from USRP o",
                                               fft_size=512, sample_rate=usrp_rate,fft_rate=display_rate)
            self.connect ((self.di,1), self.src_fft_diff)
            vbox.Add (self.src_fft_diff.win, 4, wx.EXPAND)

        if 0:
            self.src_fft_diff = fftsink.fft_sink_c (self, self.panel, title="Data from USRP d with agc",
                                               fft_size=512, sample_rate=usrp_rate,fft_rate=display_rate,avg_alpha=alpha)
            self.connect ((self.di,0),self.agc_d,self.src_fft_diff)
            #self.connect ((self.di,1),self.agc_o,(self.diff,1))
            #self.connect (self.diff,self.src_fft_diff)
            vbox.Add (self.src_fft_diff.win, 4, wx.EXPAND)

        if 0:
            self.src_fft_diff = fftsink.fft_sink_c (self, self.panel, title="diff",
                                               fft_size=512, sample_rate=usrp_rate,fft_rate=display_rate,avg_alpha=alpha)
            self.connect (self.agc_d,(self.diff,0))
            self.connect (self.agc_o,(self.diff,1))
            self.connect (self.diff,self.src_fft_diff)
            vbox.Add (self.src_fft_diff.win, 4, wx.EXPAND)

        if 0:
            post_deemph_fft = fftsink.fft_sink_f (self, self.panel, title="Post Deemph",
                                                  fft_size=512, sample_rate=demod_rate,
                                                  y_per_div=10, ref_level=-40,fft_rate=display_rate)
            self.connect (self.guts.deemph, post_deemph_fft)
            vbox.Add (post_deemph_fft.win, 4, wx.EXPAND)

        if 0:
            post_filt_fft = fftsink.fft_sink_f (self, self.panel, title="Post Filter", 
                                                fft_size=512, sample_rate=audio_rate,
                                                y_per_div=10, ref_level=-40,fft_rate=display_rate)
            self.connect (self.guts.audio_filter, post_filt)
            vbox.Add (fft_win4, 4, wx.EXPAND)
        try:
            self.knob = powermate.powermate(self.frame)
            self.rot = 0
            powermate.EVT_POWERMATE_ROTATE (self.frame, self.on_rotate)
            powermate.EVT_POWERMATE_BUTTON (self.frame, self.on_button)
        except:
            print "FYI: No Powermate or Contour Knob found"


    def on_rotate (self, event):
        self.rot += event.delta
        if (self.state == "FREQ"):
            if self.rot >= 3:
                self.set_freq(self.freq + .1e6)
                self.rot -= 3
            elif self.rot <=-3:
                self.set_freq(self.freq - .1e6)
                self.rot += 3
        else:
            step = self.volume_range()[2]
            if self.rot >= 3:
                self.set_vol(self.vol + step)
                self.rot -= 3
            elif self.rot <=-3:
                self.set_vol(self.vol - step)
                self.rot += 3
            
    def on_button (self, event):
        if event.value == 0:        # button up
            return
        self.rot = 0
        if self.state == "FREQ":
            self.state = "VOL"
        else:
            self.state = "FREQ"
        self.update_status_bar ()
        

    def set_vol (self, vol):
        g = self.volume_range()
        self.vol = max(g[0], min(g[1], vol))
        self.volume_control.set_k(10**(self.vol/10))
        self.myform['volume'].set_value(self.vol)
        self.update_status_bar ()
                                        
    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_d = usrp.tune(self.u, 0, self.subdev_d, target_freq)
        r_o = usrp.tune(self.u, 1, self.subdev_o, target_freq)
        
        if r_d:
            self.freq = target_freq
            self.myform['freq'].set_value(target_freq)         # update displayed value
            self.myform['freq_slider'].set_value(target_freq)  # update displayed value
            self.update_status_bar()
            self._set_status_msg("OK", 0)
            return True

        self._set_status_msg("Failed", 0)
        return False

    def set_gain(self, gain):
        self.myform['gain'].set_value(gain)     # update displayed value
        self.subdev_d.set_gain(gain)
        self.subdev_o.set_gain(gain)
        #print 'set gain'

    def set_phase(self, phase):
        self.myform['phase'].set_value(phase)     # update displayed value
        angle_in_radians=2.0*3.141592653538463*phase/360.0
        self.phase_shifter.set_k(complex(cos(angle_in_radians),sin(angle_in_radians)))
        self.update_status_bar ()

    def set_delay(self, delay):
        self.myform['delay'].set_value(delay)     # update displayed value
        delay_taps=[]
        for x in range(0,7):
          delay_taps.append(0.0)
        delay_taps[int(delay)+3]=1.0
        self.delay_value=delay
        self.delay_d.set_taps(delay_taps)
        print 'Blaaaaaaaaaaaaaaaa'
        self.update_status_bar ()

    def update_status_bar (self):
        msg = "Volume:%r  Setting:%s agc_d.gain:%s delay %s" % (self.vol, self.state,self.agc_d.gain(),self.delay_value)
        self._set_status_msg(msg, 1)
        #self.src_fft_d.set_baseband_freq(self.freq)
        #self.src_fft_diff.set_baseband_freq(self.freq)
        #self.src_fft_o.set_baseband_freq(self.freq)

    def volume_range(self):
        return (-20.0, 0.0, 0.5)
        

if __name__ == '__main__':
    app = stdgui.stdapp (wfm_rx_graph, "USRP WFM RX")
    app.MainLoop ()
