Weaver method SSB Modulator



The Weaver scripts were created from diagrams in this application note

Update: previous version claimed that changing the sum from gr.add_ff() to gr.sub_ff() would change the sideband - that worked in the earlier weaver graph with seperate I/Q pathways, but in this version using combined complex pathways, the sideband is changed by changing the sign of the rf local oscillator frequency. in rf_loc, change rf_LO to -rf_LO for USB. Also change the sign in calculating the carrier point rf_LO to + for USB. This can all be done with one variable 's', set to '1' for LSB and '-1' for USB.

This is the latest, final script that will likely be used on the air. I'm using this for generating SSB for PSK31 - so far I'm getting IMD of better than -27db running 20W into a dummy load and picking up on a nearby receiver. See the spectrum analysis bullet for details on spectral purity.

weaver_gen_2.py
#!/usr/bin/env python
#
#                    Weaver SSB generation
#
#               af_loc                      rf_loc
#                 |                           |
# [audio]=[f2c]--(x)-----[lpf1c]---[lpf2c]---(x)--[c2f]=(+)--- ssb out
#              af_mixc                     rf_mixc
# 
#


from gnuradio import gr
from gnuradio import usrp
from gnuradio import audio
import sys

def build_graph ():

    usrp_interp = 80
    usrp_duc = 3600e3
    sink = usrp.sink_c (0, usrp_interp)
    print "requesting: ", usrp_duc, "hz"
    sink.set_tx_freq(0, usrp_duc)
# use usrp.tx_freq(0) to get actual freq, and use that
# to set rf_LO
    actual_freq = sink.tx_freq(0)
    print "got ", actual_freq, "hz"
    sink.set_nchannels(1)
    sink.set_mux(0x98)


    target_freq = 3615e3

    offset_freq = target_freq - actual_freq
    print "Offset freq:", offset_freq

    af_LO = 1.8e3
    # set s = 1 for LSB, s = -1 for USB
    s = 1
    rf_LO = s * ( offset_freq - ( s * af_LO ))
    print "rf_LO: ", rf_LO

    af_sample_rate = 32000
    rf_sample_rate = 1600000
    fir_interp = rf_sample_rate / af_sample_rate 
    print "fir_interp: ", fir_interp

    fg = gr.flow_graph ()
    
# transmit an audio file:
#    audio_file = gr.file_source (gr.sizeof_short, "x-1_b32.sw",1)
#    audio_conv = gr.short_to_float()
#    src = gr.multiply_const_ff(.00003)
#    fg.connect(audio_file, audio_conv)
#    fg.connect(audio_conv, src)

# microphone/line-in source:
    src_mic = audio.source(af_sample_rate)
    src = gr.multiply_const_ff(13)
    fg.connect (src_mic, src)

# two-tone test:
#    src1 = gr.sig_source_f (af_sample_rate,gr.GR_SIN_WAVE,1400,1,0)
#    src = gr.sig_source_f (af_sample_rate,gr.GR_SIN_WAVE,440,1,0)
#    src = gr.add_ff()
#    fg.connect (src1, (src, 0))
#    fg.connect (src2, (src, 1))

    f2c = gr.float_to_complex()

    af_loc = gr.sig_source_c (af_sample_rate,gr.GR_COS_WAVE,af_LO,1,0)

    af_mixc = gr.multiply_cc ()

    lpf1_taps = gr.firdes.low_pass ( \
           1.0, af_sample_rate, 1.8e3, 600, gr.firdes.WIN_HAMMING)
    lpf1c = gr.fir_filter_ccf (1, lpf1_taps)

    lpf2_taps = gr.firdes.low_pass ( \
           1.0, rf_sample_rate, 5e3, 10e3, gr.firdes.WIN_HAMMING)
    lpf2c = gr.interp_fir_filter_ccf (fir_interp, lpf2_taps)

    rf_loc = gr.sig_source_c (rf_sample_rate,gr.GR_COS_WAVE,rf_LO,2,0)

    rf_mixc = gr.multiply_cc ()

    c2f = gr.complex_to_float()

    sum = gr.add_ff ()
    scale = gr.multiply_const_ff(40 * 500)
    hilbert = gr.hilbert_fc(197)

#    out = gr.file_sink (gr.sizeof_short, "ssb_mod")

    fg.connect (src, (f2c, 0))
    fg.connect (src, (f2c, 1))
    fg.connect (f2c, (af_mixc, 0))
    fg.connect (af_loc, (af_mixc, 1))
    fg.connect (af_mixc, lpf1c)
    fg.connect (lpf1c, lpf2c)
    fg.connect (lpf2c, (rf_mixc, 0))
    fg.connect (rf_loc, (rf_mixc, 1))
    fg.connect (rf_mixc, c2f)
    fg.connect ((c2f, 0), (sum, 0))
    fg.connect ((c2f, 1), (sum, 1))

    fg.connect (sum, scale)
    fg.connect (scale, hilbert)
    fg.connect (hilbert, sink)

    return fg

def main ():

    fg = build_graph ()

    fg.start ()       
    raw_input ('Press Enter to quit: ')
    fg.stop ()

if __name__ == '__main__':
    main ()