#!/usr/bin/env python
#
#                    Weaver SSB demodulation
#
#
#                                          af_loi
#                                            |
#                               --[lpf2i]---(X)---|
#                               |         af_mixi |
#                               |                 |
# signal --[freq xlating]--[split]               (+)----- ssb_demod
#          [fir filter  ]       |                 |  + USB
#                               |         af_mixq |  - LSB
#                               --[lpf2q]---(X)---|
#                                            |
#                                          af_loq
#
# This version uses TWO branches of the above, one for LSB and one for USB
# and feeds to the right & left audio out for 'stereo' independant side band

from gnuradio import gr, gru
from gnuradio import audio
from gnuradio import usrp


def build_graph (freq,scale,usrp_center,audio_file,radio_file,input_file):

    # command line options 
    if audio_file == "": SAVE_AUDIO_TO_FILE = False
    else: SAVE_AUDIO_TO_FILE = True
    if radio_file == "": SAVE_RADIO_TO_FILE = False
    else: SAVE_RADIO_TO_FILE = True
    if input_file == "": PLAY_FROM_USRP = True
    else: PLAY_FROM_USRP = False

    usrp_decim = 100
    rf_sample_rate = 64000000 / usrp_decim
    af_sample_rate = 32000
    fir_decimation = rf_sample_rate / af_sample_rate

    fg = gr.flow_graph ()

    if PLAY_FROM_USRP:
       src = usrp.source_c(0,usrp_decim)
       mux = gru.hexint(0xf0f0f0f0)
       src.set_pga(0,20)
       src.set_mux(mux)
       src.set_rx_freq(0,-usrp_center)
       tune_offset = -usrp_center - src.rx_freq(0)

    else:
       src = gr.file_source (gr.sizeof_gr_complex,input_file)
       tune_offset = 2200 # 2200 works for 3.5-4Mhz band

# save radio data to a file
    if SAVE_RADIO_TO_FILE:
       file = gr.file_sink(gr.sizeof_gr_complex, radio_file)
       fg.connect (src, file)

    rf_LO_L = usrp_center - ( freq - tune_offset ) + 1.8e3
    rf_LO_U = usrp_center - ( freq - tune_offset ) - 1.82e3
    af_LO = 1.8e3


    xlate_taps = gr.firdes.low_pass ( \
        1.0, rf_sample_rate, 20e3, 10e3, gr.firdes.WIN_HAMMING )

# LSB branch
    xlate_L = gr.freq_xlating_fir_filter_ccf ( \
        fir_decimation, xlate_taps, rf_LO_L, rf_sample_rate )

    split_L = gr.complex_to_float ()

    af_loi_L = gr.sig_source_f (af_sample_rate,gr.GR_COS_WAVE,af_LO,1,0)
    af_loq_L = gr.sig_source_f (af_sample_rate,gr.GR_SIN_WAVE,af_LO,1,0)

    lpf2_taps = gr.firdes.low_pass ( \
           1.0, af_sample_rate, 2e3, 600, gr.firdes.WIN_HAMMING)
    lpf2i_L = gr.fir_filter_fff (1, lpf2_taps)
    lpf2q_L = gr.fir_filter_fff (1, lpf2_taps)

    af_mixi_L = gr.multiply_ff ()
    af_mixq_L = gr.multiply_ff ()

    sum_L = gr.add_ff ()    # sub for USB,  add for LSB

    audio_lpf_coeffs = gr.firdes.low_pass ( \
           1.0,af_sample_rate,3000,600,gr.firdes.WIN_HAMMING)
    audio_lpf_L = gr.fir_filter_fff (1, audio_lpf_coeffs)

    scale_L = gr.multiply_const_ff(scale)

# USB branch
    xlate_U = gr.freq_xlating_fir_filter_ccf ( \
        fir_decimation, xlate_taps, rf_LO_U, rf_sample_rate )

    split_U = gr.complex_to_float ()

    af_loi_U = gr.sig_source_f (af_sample_rate,gr.GR_COS_WAVE,af_LO,1,0)
    af_loq_U = gr.sig_source_f (af_sample_rate,gr.GR_SIN_WAVE,af_LO,1,0)

    lpf2i_U = gr.fir_filter_fff (1, lpf2_taps)
    lpf2q_U = gr.fir_filter_fff (1, lpf2_taps)

    af_mixi_U = gr.multiply_ff ()
    af_mixq_U = gr.multiply_ff ()

    sum_U = gr.sub_ff ()    # sub for USB,  add for LSB

    audio_lpf_U = gr.fir_filter_fff (1, audio_lpf_coeffs)

    scale_U = gr.multiply_const_ff(scale)

# AM branch
    mag = gr.complex_to_mag()
    sel_am = gr.multiply_const_ff(0)
    scale_am = gr.multiply_const_ff(0)

# AGC lsb
    sqr1_L = gr.multiply_ff()
    int_L = gr.iir_filter_ffd ( [.004, 0], [0, .999] )
    offset_L = gr.add_const_ff(1)
    agc_L = gr.divide_ff()

# AGC usb
    sqr1_U = gr.multiply_ff()
    int_U = gr.iir_filter_ffd ( [.004, 0], [0, .999] )
    offset_U = gr.add_const_ff(1)
    agc_U = gr.divide_ff()

# sideband selection
    sel_lsb_0 = gr.multiply_const_ff(1)
    sel_lsb_1 = gr.multiply_const_ff(1)
    sel_usb_0 = gr.multiply_const_ff(0)
    sel_usb_1 = gr.multiply_const_ff(0)
    select0 = gr.add_ff()
    select1 = gr.add_ff()

    out = audio.sink (long(af_sample_rate))

    fg.connect (src, xlate_L)
    fg.connect (src, xlate_U)
    fg.connect (xlate_L, split_L)
    fg.connect (xlate_U, split_U)

    fg.connect ((split_L, 0), lpf2i_L)
    fg.connect ((split_U, 0), lpf2i_U)
    fg.connect (lpf2i_L, (af_mixi_L, 0))
    fg.connect (lpf2i_U, (af_mixi_U, 0))
    fg.connect (af_loi_L, (af_mixi_L, 1))
    fg.connect (af_loi_U, (af_mixi_U, 1))
    fg.connect (af_mixi_L, (sum_L, 0))
    fg.connect (af_mixi_U, (sum_U, 0))

    fg.connect ((split_L, 1), lpf2q_L)
    fg.connect ((split_U, 1), lpf2q_U)
    fg.connect (lpf2q_L, (af_mixq_L, 0))
    fg.connect (lpf2q_U, (af_mixq_U, 0))
    fg.connect (af_loq_L, (af_mixq_L, 1))
    fg.connect (af_loq_U, (af_mixq_U, 1))
    fg.connect (af_mixq_L, (sum_L, 1))
    fg.connect (af_mixq_U, (sum_U, 1))

    fg.connect (sum_L, audio_lpf_L)
    fg.connect (sum_U, audio_lpf_U)
    fg.connect (audio_lpf_L, scale_L)
    fg.connect (audio_lpf_U, scale_U)
# wire AGC
    fg.connect (scale_L, (sqr1_L, 0))
    fg.connect (scale_U, (sqr1_U, 0))
    fg.connect (scale_L, (sqr1_L, 1))
    fg.connect (scale_U, (sqr1_U, 1))
    fg.connect (sqr1_L, int_L)
    fg.connect (sqr1_U, int_U)
    fg.connect (int_L, offset_L)
    fg.connect (int_U, offset_U)
    fg.connect (offset_L, (agc_L, 1))
    fg.connect (offset_U, (agc_U, 1))
    fg.connect (scale_L, (agc_L, 0))
    fg.connect (scale_U, (agc_U, 0))
# wire sideband selection
    fg.connect (agc_L, sel_lsb_0)
    fg.connect (agc_U, sel_usb_0)
    fg.connect (agc_L, sel_lsb_1)
    fg.connect (agc_U, sel_usb_1)
    fg.connect (sel_lsb_0, (select0, 0))
    fg.connect (sel_usb_0, (select0, 1))
    fg.connect (sel_lsb_1, (select1, 0))
    fg.connect (sel_usb_1, (select1, 1))
    fg.connect (select0, (out, 0))
    fg.connect (select1, (out, 1))
# output to file
    if SAVE_AUDIO_TO_FILE:
       f_out = gr.file_sink(gr.sizeof_short,audio_file)
# This creates a signed-word 2-channel (stereo) file at 32000 hz
# convert to wav file with: sox -c 2 -r 32000 hfx_out.sw hfx_out.wav
       combine = gr.interleave(gr.sizeof_short)
       sc1 = gr.multiply_const_ff(64000)
       sc2 = gr.multiply_const_ff(64000)
       f2s1 = gr.float_to_short()
       f2s2 = gr.float_to_short()
       fg.connect (select0, sc1)
       fg.connect (select1, sc2)
       fg.connect (sc1, f2s1)
       fg.connect (sc2, f2s2)
       fg.connect (f2s1, (combine, 0))
       fg.connect (f2s2, (combine, 1))
       fg.connect (combine, f_out)
# wire AM
    fg.connect (xlate_L, mag)
    fg.connect (mag, sel_am)
    fg.connect (sel_am, scale_am)
    fg.connect (scale_am, (select0, 2))
    fg.connect (scale_am, (select1, 2))

    return fg, tune_offset, xlate_L, xlate_U, scale_L, scale_U, src, audio_lpf_L, audio_lpf_U, sel_lsb_0, sel_lsb_1, sel_usb_0, sel_usb_1, sel_am, scale_am

