#!/usr/bin/env python
#
# Copyright 2004 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.
#

#At 05:59 PM 12/24/2004 +0100, you wrote:
#
#> I am trying to build an AM receiver with gnuradio.
#> Can anybody tell me how to do this (example)?
#
#
#Hi Martin - here's the script I use all the time. Customise for your setup and data
#source. Currently this is for 8e6 sampling rate with a 2048 offset - my ssrp returns
#in the range 0-4096 with 0 at 2048. It takes a freq (in hz) and volume argument (values
#from .0005 for very strong signals to .05 work).   The actual AM demodulation is the
#gr.complex_to_mag()  which is followed by a 0-dc restorer.

from gnuradio import gr
from gnuradio import audio
#from gnuradio import ssrp
from gnuradio import mc4020
import sys
import math
import random

def make_amsource_virtual(fg,sampling_freq,carrier_freq):
    rf_ampl=0.001*127
    noise_ampl=0.05*127
    modulation_index=0.5
    modulation_freq=1000.0

    carrier_ampl = 127.0#0.1*sqrt(rf_ampl)#0.1*11=1.1
    modulation_ampl=127.0*modulation_index#modulation_index*sqrt(rf_ampl)#0.5*11=5.5
    modulation_offset=127#1.0*sqrt(rf_ampl)#11.0
    carrier = gr.sig_source_s (sampling_freq, gr.GR_SIN_WAVE, carrier_freq, carrier_ampl)
    modulation = gr.sig_source_s (sampling_freq, gr.GR_SIN_WAVE, modulation_freq, modulation_ampl,modulation_offset)
    modulator=gr.multiply_ss()
    rescaler=gr.divide_ss()
    scale=gr.sig_source_s (sampling_freq, gr.GR_CONST_WAVE, 127, 0)#(127*127)/rf_ampl)
    noise=gr.noise_source_s(gr.GR_UNIFORM,noise_ampl,0)
    noise_adder=gr.add_ss()
    #rfsignal=carrier*(1+modulation)
    fg.connect(carrier,(modulator,0))
    fg.connect(modulation,(modulator,1))
    #fg.connect(modulator,(rescaler,0))
    #fg.connect(scale,(rescaler,1))
    #fg.connect(rescaler,(noise_adder,0))
    #fg.connect(noise,(noise_adder,1))
    fg.connect(modulator,(noise_adder,0))
    fg.connect(noise,(noise_adder,1))
    return (noise_adder,0)

def am_modulation(samplenr):
    sampling_freq  = 50.0*1e6
    rf_amplitude=0.2*127
    noise_amplitude=0.9*127
    fc=11.0625*1e6#11.0625*1e6
    fm=1000.0
    modulation_index=0.5
    t=1.0*samplenr/sampling_freq
    two_pi=2.0*3.1415926535384626
    carrier=math.sin(two_pi*fc*t)
    modulation=math.sin(two_pi*fm*t)
    am=rf_amplitude*(1.0+modulation*modulation_index)*carrier#+random.uniform(-1.0*noise_amplitude, noise_amplitude)
    am_s=int(am)#+random.randint(-10,10)
    return am_s
def make_amsource_virtual2(fg,sampling_freq,carrier_freq):
#"map(function, sequence)" calls function(item) for each of the sequence's items and returns a list of the return values. #For example, to compute some cubes:

#>>> def cube(x): return x*x*x
#...
#>>> map(cube, range(1, 11))
#[1, 8, 27, 64, 125, 216, 343, 512, 729, 1000]
    samples_per_modulation_period=sampling_freq/1000.0
    amvector=map(am_modulation,range(0,1*samples_per_modulation_period))
    #print(amvector)
    return gr.vector_source_s(amvector,1)

def build_graph (freq,rffreq,sfactor):
        #sampling_freq = 35468950
        #sampling_freq = 50000000.0
        sampling_freq  = 50.0*1e6
        flags=mc4020.BTTV_EXTEND_RAW_LINES |mc4020.BTTV_NOGAP |mc4020.BTTV_USE_AVG_FRAME  #| mc4020.BTTV_DEC3
        # |mc4020.BTTV_DO_NOT_EMULATE_AC_COUPLING |mc4020.BTTV_OUTPUT_MULTIPLE |mc4020.BTTV_DO_NOT_EMULATE_AC_COUPLING
        if((flags & mc4020.BTTV_DEC3)==mc4020.BTTV_DEC3):
           sampling_freq=sampling_freq/3 #sampling_freq=11822983.0
        #sampling_freq=sampling_freq/4
        #audio_rate=32000
        audio_rate=64000
        real_audio_rate=32000   #16000
        cfir_decimation=int(sampling_freq/(8*audio_rate))*8 # = 368
        print(cfir_decimation)
        fg = gr.flow_graph ()

        #src0 = ssrp.source_f(0)
        #My own driver for my own hacked bttv card to have functionality similar to mc4020
        #this driver has an output multiple of 1380352
        src0 = mc4020.source (sampling_freq,flags, "/dev/video0");
        actualtunerfreq=src0.set_RF_freq(rffreq)
        freq=freq+(rffreq - actualtunerfreq)
         
        #src0=make_amsource_virtual2(fg,sampling_freq,freq)
        #src0=gr.noise_source_s(gr.GR_UNIFORM ,127,0) #GR_UNIFORM 	GR_GAUSSIAN 	GR_LAPLACIAN 	GR_IMPULSE 
        #print(freq/1e6)
        #print(actualtunerfreq/1e6)
#       src0 = gr.file_source (gr.sizeof_short, "am_band", 1)
#        adj_in = gr.add_const_ss(-2048)
        adj_in = gr.add_const_ss(0)  #dummy adj_in does nothing (add 0 to the signal) but has a big effect on the minimal bandwith of the fir filter I can use
        #keep_one_in_n=gr.keep_one_in_n(gr.sizeof_short,4)
        # compute FIR filter taps
        channel_coeffs = \
                gr.firdes.low_pass (
                  1.0,                  # gain
                  sampling_freq,
                  200e3,                 # low pass cutoff
                  1850.001e3,                 # width of transition band  
                                             # 123663.52 gives ntaps=317, if not using adj_in audio stops after about a second
                                             # 123663.53 gives ntaps=315, if not using adj_in audio keeps working 
                                             # 2436.1 gives ntaps=16017,if using dummy adj_in gives no audio at all 
                                             # 2436.2 gives ntaps=16015,if using dummy adj_in audio keeps working 
                  gr.firdes.WIN_HAMMING )

        # input: short; output: complex
        chan_filter1 = \
                gr.freq_xlating_fir_filter_scf (
                  cfir_decimation,
                  channel_coeffs,
                  freq,
                  sampling_freq )

        am_demod = gr.complex_to_mag ()

        diff = gr.fir_filter_fff ( 1, [1, -1] )
        integ = gr.iir_filter_ffd ( [1, 0], [0, .999] )
        
        scale = gr.multiply_const_ff (sfactor)

        audio_lp_coeffs = gr.firdes.low_pass (
                1.0, audio_rate, 6e3, 6000, #6e3#1.0, audio_rate*0.75, audio_rate/4, 600, #6e3
                gr.firdes.WIN_HAMMING )
        
        audio_lp = gr.fir_filter_fff (audio_rate/real_audio_rate, audio_lp_coeffs )
        #one_in_n=gr.keep_one_in_n(gr.sizeof_float,audio_rate/real_audio_rate)
#       dst = gr.file_sink (gr.sizeof_float, "950_am_out")
        dst = audio.sink ( real_audio_rate )
        #avg = gr.fir_filter_scc ( 4, [complex(1,0), complex(1,0),complex(1,0),complex(1,0)] )
        #fg.connect ( src0, adj_in )  #uncomment this line to use dummy adj_in
        #fg.connect ( adj_in,  keep_one_in_n) #uncomment this line to use dummy adj_in
        #fg.connect ( src0, chan_filter1  ) #comment this line to use dummy adj_in
        #fg.connect(avg,chan_filter1)
        fg.connect(src0,chan_filter1)
        fg.connect ( chan_filter1, am_demod )
        #fg.connect ( am_demod, dst )
        fg.connect ( am_demod, diff )
        fg.connect ( diff, integ )
        #fg.connect ( integ, dst )
        fg.connect ( integ, scale )
        #fg.connect ( scale, dst )
        fg.connect ( scale, audio_lp )
        fg.connect ( audio_lp, dst )
        ##fg.connect ( audio_lp, one_in_n )
        ##fg.connect(one_in_n,dst)

        return fg

def main (args):
        
        nargs = len (args)
        if nargs == 3:
                freq=float (args[0])* 1e6
                rffreq=float (args[1])* 1e6
                sfactor=float (args[2])* 1e6
        else:
                sys.stderr.write ('usage: am_demod.py freq rffreq scale\n')
                sys.exit (1)
        fg = build_graph(freq,rffreq,sfactor)
        fg.start()
        raw_input ('Press Enter to quit')
        fg.stop()

if __name__ == '__main__':
        main (sys.argv[1:])



