#!/usr/bin/env python
# -*- coding: ANSI_X3.4-1968 -*-
# generated by wxGlade 0.4 on Tue Mar 14 10:16:06 2006
#
# Copyright 2006 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 3, 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., 51 Franklin Street,
# Boston, MA 02110-1301, USA.
#
#-----------------------------------------------------------------
#
#                 +-->(fft)
#                 |
# (src)->(xlate)--+->(audio filter)--+-->(sel_am)-+--------------+
#                                    |            |              |
#                                    |          (pll)            |
#                                    |            |              |
#                                    |   (pll_carrier_scale)     |
#                                    |            |              |
#                                    |   (pll_carrier_filter)    |
#                                    |            |              |
#                                    |            +--(fft2)      |
#                                    |            |              |
#                                    |       +--(c2f3)--+        |
#                                    |       |          |        |
#                                    |   (phaser1)  (phaser2)    |
#                                    |       |          |        |
#                                    |       +--(f2c)---+        |
#                                    |            |              V
#                                    V            +---------->(am_det)
#                                  (c2f)                         |
#                                    |                         (c2f2)
#                                    |                           |
#                                    +-->(sel_sb)------------>(combine)
#                                                                |
#                                                                V
#                                  +--------------------------(scale)
#                                  |                             |
#                                  |                             |
#                                  |                            +++
#                                  V                            | |
#                                (agc)<--(offset)<--(intr)<---(sqr1)
#                                  |
#                                  V
#                                (dst)
#                                                       
#----------------------------------------------------------------------
#
# Versions 2.2.1 adds loop antenna automatic tuner
#
# 2.3.1 adds web control, made AM Sync display optional,
# added more comments.
#
# 2.4.1 updates usrp interface to support auto subdev
# 
# 2.8.1 changed saved file format from 8-byte complex to
# 4-byte short for obvious storage space savings.

# Web server control disabled by default. Do not enable
# until directory structure and scripts are in place.
WEB_CONTROL = False

# Controls display of AM Sync Carrier - turn off for smaller
# window if not needed
AM_SYNC_DISPLAY = False

import os, wx, sys, math
import wx.lib.evtmgr as em
from gnuradio.wxgui import powermate, fftsink2
from gnuradio import gr, audio, eng_notation, usrp, gru
from gnuradio.eng_option import eng_option
from optparse import OptionParser

ID_BUTTON_1 = wx.NewId()	# LSB button
ID_BUTTON_2 = wx.NewId()	# USB
ID_BUTTON_3 = wx.NewId()	# AM
ID_BUTTON_4 = wx.NewId()	# CW
ID_BUTTON_5 = wx.NewId()	# Powermate controls: Upper audio freq cutoff
ID_BUTTON_6 = wx.NewId()	#  "                  Lower audio freq cutoff
ID_BUTTON_7 = wx.NewId()	#  "                  Frequency 
ID_BUTTON_8 = wx.NewId()	#  "                  Volume
ID_BUTTON_9 = wx.NewId()	#  "                  Time
ID_BUTTON_10 = wx.NewId()	# Time Seek Forwards
ID_BUTTON_11 = wx.NewId()	# Time Seek Backwards
ID_BUTTON_12 = wx.NewId()	# Automatic Antenna Tune (AT) enable
ID_BUTTON_13 = wx.NewId()	# AT Calibrate point
ID_BUTTON_14 = wx.NewId()	# AT Reset
ID_TEXT_1 = wx.NewId()		# Band Center, USRP ddc Freq
ID_SPIN_1 = wx.NewId()		# Frequency display and control
ID_SLIDER_1 = wx.NewId()	# Upper audio freq cutoff
ID_SLIDER_2 = wx.NewId()	# Lower audio freq cutoff
ID_SLIDER_3 = wx.NewId()	# Frequency 
ID_SLIDER_4 = wx.NewId()	# Volume
ID_SLIDER_5 = wx.NewId()	# Programmable Gain Amp, PGA, RF gain
ID_SLIDER_6 = wx.NewId()	# AM Sync carrier level
ID_SLIDER_7 = wx.NewId()	# AT control voltage output
ID_EXIT	= wx.NewId()		# Menu Exit


def pick_subdevice(u):
    """
    The user didn't specify a subdevice on the command line.
    If there's a daughterboard on A, select A.
    If there's a daughterboard on B, select B.
    Otherwise, select A.
    """
    if u.db(0, 0).dbid() >= 0:       # dbid is < 0 if there's no d'board or a problem
        return (0, 0)
    if u.db(1, 0).dbid() >= 0:
        return (1, 0)
    return (0, 0)


class MyFrame(wx.Frame):
    def __init__(self, *args, **kwds):
        # begin wxGlade: MyFrame.__init__
        kwds["style"] = wx.DEFAULT_FRAME_STYLE
        wx.Frame.__init__(self, *args, **kwds)
        
        # Menu Bar
        self.frame_1_menubar = wx.MenuBar()
        self.SetMenuBar(self.frame_1_menubar)
        wxglade_tmp_menu = wx.Menu()
        self.Exit = wx.MenuItem(wxglade_tmp_menu, ID_EXIT, "Exit", "Exit", wx.ITEM_NORMAL)
        wxglade_tmp_menu.AppendItem(self.Exit)
        self.frame_1_menubar.Append(wxglade_tmp_menu, "File")
        # Menu Bar end
        self.panel_1 = wx.Panel(self, -1)
        self.button_1 = wx.Button(self, ID_BUTTON_1, "LSB")
        self.button_2 = wx.Button(self, ID_BUTTON_2, "USB")
        self.button_3 = wx.Button(self, ID_BUTTON_3, "AM")
        self.button_4 = wx.Button(self, ID_BUTTON_4, "CW")
        self.button_5 = wx.ToggleButton(self, ID_BUTTON_5, "Upper")
        self.slider_1 = wx.Slider(self, ID_SLIDER_1, 0, -15799, 15799, style=wx.SL_HORIZONTAL|wx.SL_LABELS)
        self.button_6 = wx.ToggleButton(self, ID_BUTTON_6, "Lower")
        self.slider_2 = wx.Slider(self, ID_SLIDER_2, 0, -15799, 15799, style=wx.SL_HORIZONTAL|wx.SL_LABELS)
        self.panel_5 = wx.Panel(self, -1)
        self.label_1 = wx.StaticText(self, -1, " Band\nCenter")
        self.text_ctrl_1 = wx.TextCtrl(self, ID_TEXT_1, "")
        self.panel_6 = wx.Panel(self, -1)
        self.panel_7 = wx.Panel(self, -1)
        self.panel_2 = wx.Panel(self, -1)
        self.button_7 = wx.ToggleButton(self, ID_BUTTON_7, "Freq")
        self.slider_3 = wx.Slider(self, ID_SLIDER_3, 3000, 0, 6000)
        self.spin_ctrl_1 = wx.SpinCtrl(self, ID_SPIN_1, "", min=0, max=100)
        self.button_8 = wx.ToggleButton(self, ID_BUTTON_8, "Vol")
        self.slider_4 = wx.Slider(self, ID_SLIDER_4, 0, 0, 500)
        self.slider_5 = wx.Slider(self, ID_SLIDER_5, 0, 0, 20)
        self.button_9 = wx.ToggleButton(self, ID_BUTTON_9, "Time")
        self.button_11 = wx.Button(self, ID_BUTTON_11, "Rew")
        self.button_10 = wx.Button(self, ID_BUTTON_10, "Fwd")
        self.panel_3 = wx.Panel(self, -1)
        self.label_2 = wx.StaticText(self, -1, "PGA               ")
        self.panel_4 = wx.Panel(self, -1)
        self.panel_8 = wx.Panel(self, -1)
        self.panel_9 = wx.Panel(self, -1)
        self.label_3 = wx.StaticText(self, -1, "AM Sync\nCarrier")
        self.slider_6 = wx.Slider(self, ID_SLIDER_6, 50, 0, 200, style=wx.SL_HORIZONTAL|wx.SL_LABELS)
        self.label_4 = wx.StaticText(self, -1, "Antenna Tune")
        self.slider_7 = wx.Slider(self, ID_SLIDER_7, 1575, 950, 2200, style=wx.SL_HORIZONTAL|wx.SL_LABELS)
        self.panel_10 = wx.Panel(self, -1)
        self.button_12 = wx.ToggleButton(self, ID_BUTTON_12, "Auto Tune")
        self.button_13 = wx.Button(self, ID_BUTTON_13, "Calibrate")
        self.button_14 = wx.Button(self, ID_BUTTON_14, "Reset")
        self.panel_11 = wx.Panel(self, -1)
        self.panel_12 = wx.Panel(self, -1)

        self.__set_properties()
        self.__do_layout()
        # end wxGlade

        parser = OptionParser (option_class=eng_option)
        parser.add_option ("-c", "--ddc-freq", type="eng_float", default=3.9e6,
                           help="set Rx DDC frequency to FREQ", metavar="FREQ")
        parser.add_option ("-a", "--audio_file", default="",
                           help="audio output file", metavar="FILE")
        parser.add_option ("-r", "--radio_file", default="",
                           help="radio output file", metavar="FILE")
        parser.add_option ("-i", "--input_file", default="",
                           help="radio input file", metavar="FILE")
        parser.add_option ("-d", "--decim", type="int", default=250,
                           help="USRP decimation")
        parser.add_option("-R", "--rx-subdev-spec", type="subdev", default=None,                          help="select USRP Rx side A or B (default=first one with a daughterboard)")
        (options, args) = parser.parse_args ()

        self.usrp_center = options.ddc_freq
        usb_rate = 64e6 / options.decim
        self.slider_range = usb_rate * 0.9375
        self.f_lo = self.usrp_center - (self.slider_range/2)
        self.f_hi = self.usrp_center + (self.slider_range/2)
	self.af_sample_rate = 32000
	fir_decim = long (usb_rate / self.af_sample_rate)

        # data point arrays for antenna tuner
        self.xdata = []
        self.ydata = []

	self.tb = gr.top_block()

        # radio variables, initial conditions
        self.frequency = self.usrp_center
        # these map the frequency slider (0-6000) to the actual range
        self.f_slider_offset = self.f_lo
        self.f_slider_scale = 10000/options.decim
        self.spin_ctrl_1.SetRange(self.f_lo,self.f_hi)
        self.text_ctrl_1.SetValue(str(int(self.usrp_center)))
        self.slider_5.SetValue(0) 
	self.AM_mode = False

        self.slider_3.SetValue((self.frequency-self.f_slider_offset)/self.f_slider_scale)
        self.spin_ctrl_1.SetValue(int(self.frequency))

        POWERMATE = True
        try:
            self.pm = powermate.powermate(self)
        except:
            sys.stderr.write("Unable to find PowerMate or Contour Shuttle\n")
            POWERMATE = False

        if POWERMATE:
          powermate.EVT_POWERMATE_ROTATE(self, self.on_rotate)
          powermate.EVT_POWERMATE_BUTTON(self, self.on_pmButton)
	self.active_button = 7

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

        if self.PLAY_FROM_USRP:
           self.src = usrp.source_s(decim_rate=options.decim)
	   if options.rx_subdev_spec is None:
              options.rx_subdev_spec = pick_subdevice(self.src)
           self.src.set_mux(usrp.determine_rx_mux_value(self.src, options.rx_subdev_spec))
	   self.subdev = usrp.selected_subdev(self.src, options.rx_subdev_spec)
           self.src.tune(0, self.subdev, self.usrp_center)
           self.tune_offset = 0 # -self.usrp_center - self.src.rx_freq(0)

        else:
           self.src = gr.file_source (gr.sizeof_short,options.input_file)
           self.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_short, options.radio_file)
           self.tb.connect (self.src, file)

	# 2nd DDC
        xlate_taps = gr.firdes.low_pass ( \
           1.0, usb_rate, 16e3, 4e3, gr.firdes.WIN_HAMMING )
        self.xlate = gr.freq_xlating_fir_filter_ccf ( \
           fir_decim, xlate_taps, self.tune_offset, usb_rate )

        # convert rf data in interleaved short int form to complex
        s2ss = gr.stream_to_streams(gr.sizeof_short,2)
        s2f1 = gr.short_to_float()
        s2f2 = gr.short_to_float()
        src_f2c = gr.float_to_complex()
        self.tb.connect(self.src,s2ss)
        self.tb.connect((s2ss,0),s2f1)
        self.tb.connect((s2ss,1),s2f2)
        self.tb.connect(s2f1,(src_f2c,0))
        self.tb.connect(s2f2,(src_f2c,1))


	# Complex Audio filter
        audio_coeffs = gr.firdes.complex_band_pass (
                1.0,    # gain
                self.af_sample_rate, # sample rate
                -3000,    # low cutoff
                0,   # high cutoff
                100,    # transition
                gr.firdes.WIN_HAMMING)  # window
	self.slider_1.SetValue(0)
	self.slider_2.SetValue(-3000)

        self.audio_filter = gr.fir_filter_ccc ( 1, audio_coeffs)

	# Main +/- 16Khz spectrum display
        self.fft = fftsink2.fft_sink_c (self.panel_2, fft_size=512, sample_rate=self.af_sample_rate, average=True, size=(640,240))

	# AM Sync carrier 
	if AM_SYNC_DISPLAY:
	   self.fft2 = fftsink.fft_sink_c (self.tb, self.panel_9, y_per_div=20, fft_size=512, sample_rate=self.af_sample_rate, average=True, size=(640,240))

        c2f = gr.complex_to_float()

	# AM branch
	self.sel_am = gr.multiply_const_cc(0)
	# the following frequencies turn out to be in radians/sample
	# gr.pll_refout_cc(alpha,beta,min_freq,max_freq)
	# suggested alpha = X, beta = .25 * X * X
	pll = gr.pll_refout_cc(.5,.0625,(2.*math.pi*7.5e3/self.af_sample_rate),(2.*math.pi*6.5e3/self.af_sample_rate))
	self.pll_carrier_scale = gr.multiply_const_cc(complex(10,0))
	am_det = gr.multiply_cc()
	# these are for converting +7.5kHz to -7.5kHz
	# for some reason gr.conjugate_cc() adds noise ??
	c2f2 = gr.complex_to_float()
	c2f3 = gr.complex_to_float()
	f2c = gr.float_to_complex()
	phaser1 = gr.multiply_const_ff(1)
	phaser2 = gr.multiply_const_ff(-1)

	# filter for pll generated carrier
        pll_carrier_coeffs = gr.firdes.complex_band_pass (
                2.0,    # gain
                self.af_sample_rate, # sample rate
                7400,    # low cutoff
                7600,   # high cutoff
                100,    # transition
                gr.firdes.WIN_HAMMING)  # window

        self.pll_carrier_filter = gr.fir_filter_ccc ( 1, pll_carrier_coeffs)

	self.sel_sb = gr.multiply_const_ff(1)
	combine = gr.add_ff()

	#AGC
	sqr1 = gr.multiply_ff()
	intr = gr.iir_filter_ffd ( [.004, 0], [0, .999] )
	offset = gr.add_const_ff(1)
	agc = gr.divide_ff()


        self.scale = gr.multiply_const_ff(0.00001)
        dst = audio.sink(long(self.af_sample_rate))

        self.tb.connect(src_f2c,self.xlate,self.fft)
        self.tb.connect(self.xlate,self.audio_filter,self.sel_am,(am_det,0))
	self.tb.connect(self.sel_am,pll,self.pll_carrier_scale,self.pll_carrier_filter,c2f3)
	self.tb.connect((c2f3,0),phaser1,(f2c,0))
	self.tb.connect((c2f3,1),phaser2,(f2c,1))
	self.tb.connect(f2c,(am_det,1))
	self.tb.connect(am_det,c2f2,(combine,0))
	self.tb.connect(self.audio_filter,c2f,self.sel_sb,(combine,1))
	if AM_SYNC_DISPLAY:
	  self.tb.connect(self.pll_carrier_filter,self.fft2)
	self.tb.connect(combine,self.scale)
	self.tb.connect(self.scale,(sqr1,0))
	self.tb.connect(self.scale,(sqr1,1))
	self.tb.connect(sqr1, intr, offset, (agc, 1))
	self.tb.connect(self.scale,(agc, 0))
	self.tb.connect(agc,dst)

	if SAVE_AUDIO_TO_FILE:
	  f_out = gr.file_sink(gr.sizeof_short,options.audio_file)
	  sc1 = gr.multiply_const_ff(64000)
	  f2s1 = gr.float_to_short()
	  self.tb.connect(agc,sc1,f2s1,f_out)

        self.tb.start()

        # for mouse position reporting on fft display
        em.eventManager.Register(self.Mouse, wx.EVT_MOTION, self.fft.win)
        # and left click to re-tune
        em.eventManager.Register(self.Click, wx.EVT_LEFT_DOWN, self.fft.win)

        # start a timer to check for web commands
        if WEB_CONTROL:
	   self.timer = UpdateTimer(self, 1000) # every 1000 mSec, 1 Sec


	wx.EVT_BUTTON(self,ID_BUTTON_1,self.set_lsb)
	wx.EVT_BUTTON(self,ID_BUTTON_2,self.set_usb)
	wx.EVT_BUTTON(self,ID_BUTTON_3,self.set_am)
	wx.EVT_BUTTON(self,ID_BUTTON_4,self.set_cw)
	wx.EVT_BUTTON(self,ID_BUTTON_10,self.fwd)
	wx.EVT_BUTTON(self,ID_BUTTON_11,self.rew)
        wx.EVT_BUTTON(self, ID_BUTTON_13, self.AT_calibrate)
        wx.EVT_BUTTON(self, ID_BUTTON_14, self.AT_reset)
	wx.EVT_TOGGLEBUTTON(self,ID_BUTTON_5,self.on_button)
	wx.EVT_TOGGLEBUTTON(self,ID_BUTTON_6,self.on_button)
	wx.EVT_TOGGLEBUTTON(self,ID_BUTTON_7,self.on_button)
	wx.EVT_TOGGLEBUTTON(self,ID_BUTTON_8,self.on_button)
	wx.EVT_TOGGLEBUTTON(self,ID_BUTTON_9,self.on_button)
        wx.EVT_SLIDER(self,ID_SLIDER_1,self.set_filter)
        wx.EVT_SLIDER(self,ID_SLIDER_2,self.set_filter)
        wx.EVT_SLIDER(self,ID_SLIDER_3,self.slide_tune)
        wx.EVT_SLIDER(self,ID_SLIDER_4,self.set_volume)
	wx.EVT_SLIDER(self,ID_SLIDER_5,self.set_pga)
	wx.EVT_SLIDER(self,ID_SLIDER_6,self.am_carrier)
        wx.EVT_SLIDER(self,ID_SLIDER_7,self.antenna_tune)
        wx.EVT_SPINCTRL(self,ID_SPIN_1,self.spin_tune)

        wx.EVT_MENU(self, ID_EXIT,  self.TimeToQuit)

    def __set_properties(self):
        # begin wxGlade: MyFrame.__set_properties
        self.SetTitle("HF Explorer 2")
        self.slider_1.SetMinSize((450, 38))
        self.slider_2.SetMinSize((450, 38))
        self.panel_2.SetMinSize((640, 240))
        self.button_7.SetValue(1)
        self.slider_3.SetMinSize((450, 19))
        self.slider_4.SetMinSize((275, 19))
        self.slider_5.SetMinSize((275, 19))
	if AM_SYNC_DISPLAY:
           self.panel_9.SetMinSize((640, 240))
        self.slider_6.SetMinSize((300, 38))
        self.slider_7.SetMinSize((400, 38))
        # end wxGlade

    def __do_layout(self):
        # begin wxGlade: MyFrame.__do_layout
        sizer_1 = wx.BoxSizer(wx.VERTICAL)
        grid_sizer_1 = wx.FlexGridSizer(11, 2, 0, 0)
        sizer_7 = wx.BoxSizer(wx.HORIZONTAL)
        sizer_5 = wx.BoxSizer(wx.HORIZONTAL)
        sizer_4 = wx.BoxSizer(wx.HORIZONTAL)
        sizer_3 = wx.BoxSizer(wx.HORIZONTAL)
        sizer_6 = wx.BoxSizer(wx.VERTICAL)
        sizer_2 = wx.BoxSizer(wx.HORIZONTAL)
        grid_sizer_1.Add(self.panel_1, 1, wx.EXPAND, 0)
        sizer_2.Add(self.button_1, 0, wx.ADJUST_MINSIZE, 0)
        sizer_2.Add(self.button_2, 0, wx.ADJUST_MINSIZE, 0)
        sizer_2.Add(self.button_3, 0, wx.ADJUST_MINSIZE, 0)
        sizer_2.Add(self.button_4, 0, wx.ADJUST_MINSIZE, 0)
        grid_sizer_1.Add(sizer_2, 1, wx.EXPAND, 0)
        grid_sizer_1.Add(self.button_5, 0, wx.ADJUST_MINSIZE, 0)
        grid_sizer_1.Add(self.slider_1, 0, wx.ALIGN_CENTER_HORIZONTAL|wx.ADJUST_MINSIZE, 0)
        grid_sizer_1.Add(self.button_6, 0, wx.ADJUST_MINSIZE, 0)
        grid_sizer_1.Add(self.slider_2, 0, wx.ALIGN_CENTER_HORIZONTAL|wx.ADJUST_MINSIZE, 0)
        sizer_6.Add(self.panel_5, 1, wx.EXPAND, 0)
        sizer_6.Add(self.label_1, 0, wx.ALIGN_CENTER_HORIZONTAL|wx.ADJUST_MINSIZE, 0)
        sizer_6.Add(self.text_ctrl_1, 0, wx.ADJUST_MINSIZE, 0)
        sizer_6.Add(self.panel_6, 1, wx.EXPAND, 0)
        sizer_6.Add(self.panel_7, 1, wx.EXPAND, 0)
        grid_sizer_1.Add(sizer_6, 1, wx.EXPAND, 0)
        grid_sizer_1.Add(self.panel_2, 1, wx.EXPAND, 0)
        grid_sizer_1.Add(self.button_7, 0, wx.ADJUST_MINSIZE, 0)
        sizer_3.Add(self.slider_3, 0, wx.ADJUST_MINSIZE, 0)
        sizer_3.Add(self.spin_ctrl_1, 0, wx.ADJUST_MINSIZE, 0)
        grid_sizer_1.Add(sizer_3, 1, wx.EXPAND, 0)
        grid_sizer_1.Add(self.button_8, 0, wx.ADJUST_MINSIZE, 0)
        sizer_4.Add(self.slider_4, 0, wx.ADJUST_MINSIZE, 0)
        sizer_4.Add(self.slider_5, 0, wx.ADJUST_MINSIZE, 0)
        grid_sizer_1.Add(sizer_4, 1, wx.EXPAND, 0)
        grid_sizer_1.Add(self.button_9, 0, wx.ADJUST_MINSIZE, 0)
        sizer_5.Add(self.button_11, 0, wx.ADJUST_MINSIZE, 0)
        sizer_5.Add(self.button_10, 0, wx.ADJUST_MINSIZE, 0)
        sizer_5.Add(self.panel_3, 1, wx.EXPAND, 0)
        sizer_5.Add(self.label_2, 0, wx.ADJUST_MINSIZE, 0)
        sizer_5.Add(self.panel_4, 1, wx.EXPAND, 0)
        grid_sizer_1.Add(sizer_5, 1, wx.EXPAND, 0)
        grid_sizer_1.Add(self.panel_8, 1, wx.EXPAND, 0)
        grid_sizer_1.Add(self.panel_9, 1, wx.EXPAND, 0)
        grid_sizer_1.Add(self.label_3, 0, wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 0)
        grid_sizer_1.Add(self.slider_6, 0, wx.ADJUST_MINSIZE, 0)
        grid_sizer_1.Add(self.label_4, 0, wx.ALIGN_BOTTOM|wx.ADJUST_MINSIZE, 0)
        grid_sizer_1.Add(self.slider_7, 0, wx.ADJUST_MINSIZE, 0)
        grid_sizer_1.Add(self.panel_10, 1, wx.EXPAND, 0)
        sizer_7.Add(self.button_12, 0, wx.ADJUST_MINSIZE, 0)
        sizer_7.Add(self.button_13, 0, wx.ADJUST_MINSIZE, 0)
        sizer_7.Add(self.button_14, 0, wx.ADJUST_MINSIZE, 0)
        sizer_7.Add(self.panel_11, 1, wx.EXPAND, 0)
        sizer_7.Add(self.panel_12, 1, wx.EXPAND, 0)
        grid_sizer_1.Add(sizer_7, 1, wx.EXPAND, 0)
        sizer_1.Add(grid_sizer_1, 1, wx.EXPAND, 0)
        self.SetAutoLayout(True)
        self.SetSizer(sizer_1)
        sizer_1.Fit(self)
        sizer_1.SetSizeHints(self)
        self.Layout()
        # end wxGlade

    # Menu exit
    def TimeToQuit(self, event):
        self.tb.stop()
        self.Close(True)

    # Powermate being turned
    def on_rotate(self, event):
	if self.active_button == 5:
	   self.slider_1.SetValue(self.slider_1.GetValue()+event.delta)
           if self.slider_2.GetValue() > (self.slider_1.GetValue() - 200) :
              self.slider_2.SetValue(self.slider_1.GetValue() - 200)
	   self.filter()
	if self.active_button == 6:
	   self.slider_2.SetValue(self.slider_2.GetValue()+event.delta)
	   if self.slider_1.GetValue() < (self.slider_2.GetValue() + 200) :
	      self.slider_1.SetValue(self.slider_2.GetValue() + 200)
	   self.filter()
	if self.active_button == 7:
           new = max(0, min(6000, self.slider_3.GetValue() + event.delta))
           self.slider_3.SetValue(new)
           self.frequency = (self.f_slider_scale * new) + self.f_slider_offset
           self.spin_ctrl_1.SetValue(self.frequency)
	   if self.AM_mode == False:
             self.xlate.set_center_freq( self.usrp_center - (self.frequency - self.tune_offset))
	   else:
	     self.xlate.set_center_freq( self.usrp_center - (self.frequency - self.tune_offset - 7.5e3))
	   if self.button_12.GetValue():
	      self.auto_antenna_tune()
	if self.active_button == 8:
           new = max(0, min(500, self.slider_4.GetValue() + event.delta))
           self.slider_4.SetValue(new)
           self.scale.set_k(math.pow(10.,((self.slider_4.GetValue()-500.)/100.)))
	if self.active_button == 9:
           if self.PLAY_FROM_USRP == False:
              if event.delta == -1:
                 self.src.seek(-1000000,gr.SEEK_CUR)
              elif event.delta == 1:
                 self.src.seek(1000000,gr.SEEK_CUR)


    # Powermate pressed to switch controlled function
    def on_pmButton(self, event):
	if event.value == 0:
	   if self.active_button == 5:
	      self.active_button = 6
	      self.button_5.SetValue(False)
	      self.button_6.SetValue(True)
	   elif self.active_button == 6:
	      self.active_button = 7
	      self.button_6.SetValue(False)
	      self.button_7.SetValue(True)
	   elif self.active_button == 7:
	      self.active_button = 8
	      self.button_7.SetValue(False)
	      self.button_8.SetValue(True)
	   elif self.active_button == 8:
	      self.active_button = 9
	      self.button_8.SetValue(False)
	      self.button_9.SetValue(True)
	   elif self.active_button == 9:
	      self.active_button = 5
	      self.button_9.SetValue(False)
	      self.button_5.SetValue(True)

    # Clicking one PM control button turns the rest off
    def on_button(self, event):
	id = event.GetId()
	if id == ID_BUTTON_5:
	   self.active_button = 5
	   self.button_6.SetValue(False)
	   self.button_7.SetValue(False)
	   self.button_8.SetValue(False)
	   self.button_9.SetValue(False)
	if id == ID_BUTTON_6:
	   self.active_button = 6
           self.button_5.SetValue(False)
           self.button_7.SetValue(False)
           self.button_8.SetValue(False)
           self.button_9.SetValue(False)
        if id == ID_BUTTON_7:
           self.active_button = 7
           self.button_5.SetValue(False)
           self.button_6.SetValue(False)
           self.button_8.SetValue(False)
           self.button_9.SetValue(False)
        if id == ID_BUTTON_8:
           self.active_button = 8
           self.button_5.SetValue(False)
           self.button_6.SetValue(False)
           self.button_7.SetValue(False)
           self.button_9.SetValue(False)
        if id == ID_BUTTON_9:
           self.active_button = 9
           self.button_5.SetValue(False)
           self.button_6.SetValue(False)
           self.button_7.SetValue(False)
           self.button_8.SetValue(False)

    # Make sure filter settings are legal
    def set_filter(self, event):
        slider = event.GetId()
        slider1 = self.slider_1.GetValue()
        slider2 = self.slider_2.GetValue()
        if slider == ID_SLIDER_1:
           if slider2 > (self.slider_1.GetValue() - 200) :
              self.slider_2.SetValue(slider1 - 200)
        elif slider == ID_SLIDER_2:
           if slider1 < (self.slider_2.GetValue() + 200) :
              self.slider_1.SetValue(slider2 + 200)
	self.filter()

    # Calculate taps and apply
    def filter(self):
        audio_coeffs = gr.firdes.complex_band_pass (
                1.0,    # gain
                self.af_sample_rate, # sample rate
                self.slider_2.GetValue(),    # low cutoff
                self.slider_1.GetValue(),   # high cutoff
                100,    # transition
                gr.firdes.WIN_HAMMING)  # window
        self.audio_filter.set_taps(audio_coeffs)

    def set_lsb(self, event):
	self.AM_mode = False
	self.xlate.set_center_freq( self.usrp_center - (self.frequency - self.tune_offset))
	self.sel_sb.set_k(1)
	self.sel_am.set_k(0)
	self.slider_1.SetValue(0)
	self.slider_2.SetValue(-3000)
	self.filter()

    def set_usb(self, event):
	self.AM_mode = False
	self.xlate.set_center_freq( self.usrp_center - (self.frequency - self.tune_offset))
	self.sel_sb.set_k(1)
	self.sel_am.set_k(0)
	self.slider_1.SetValue(3000)
	self.slider_2.SetValue(0)
	self.filter()

    def set_am(self, event):
	self.AM_mode = True
	self.xlate.set_center_freq( self.usrp_center - (self.frequency - self.tune_offset - 7.5e3))
	self.sel_sb.set_k(0)
	self.sel_am.set_k(1)
	self.slider_1.SetValue(12500)
	self.slider_2.SetValue(2500)
	self.filter()

    def set_cw(self, event):
	self.AM_mode = False
	self.xlate.set_center_freq( self.usrp_center - (self.frequency - self.tune_offset))
	self.AM_mode = False
	self.sel_sb.set_k(1)
	self.sel_am.set_k(0)
	self.slider_1.SetValue(-400)
	self.slider_2.SetValue(-800)
	self.filter()

    def set_volume(self, event):
        self.scale.set_k(math.pow(10.,((self.slider_4.GetValue()-500.)/100.)))

    def set_pga(self,event):
	if self.PLAY_FROM_USRP:
	   self.subdev.set_gain(self.slider_5.GetValue())

    def slide_tune(self, event):
        self.frequency = (self.f_slider_scale * self.slider_3.GetValue()) + self.f_slider_offset
	if self.AM_mode == False:
          self.xlate.set_center_freq( self.usrp_center - (self.frequency - self.tune_offset))
	else:
	  self.xlate.set_center_freq( self.usrp_center - (self.frequency - self.tune_offset - 7.5e3))
        self.spin_ctrl_1.SetValue(self.frequency)
	if self.button_12.GetValue():
	   self.auto_antenna_tune()

    def spin_tune(self, event):
	self.frequency = self.spin_ctrl_1.GetValue()
	if self.AM_mode == False:
           self.xlate.set_center_freq( self.usrp_center - (self.frequency - self.tune_offset))
	else:
	   self.xlate.set_center_freq( self.usrp_center - (self.frequency - self.tune_offset - 7.5e3))
        self.slider_3.SetValue(int((self.frequency-self.f_slider_offset)/self.f_slider_scale))
	if self.button_12.GetValue():
	   self.auto_antenna_tune()

    # Seek forwards in file
    def fwd(self, event):
	if self.PLAY_FROM_USRP == False:
	   self.src.seek(10000000,gr.SEEK_CUR)

    # Seek backwards in file
    def rew(self, event):
	if self.PLAY_FROM_USRP == False:
	   self.src.seek(-10000000,gr.SEEK_CUR)

    # Mouse over fft display - show frequency in tooltip
    def Mouse(self,event):
	if self.AM_mode:
	   fRel = ( event.GetX() - 330. ) / 14.266666 - 7.5
	else:
	   fRel = ( event.GetX() - 330. ) / 14.266666 
        self.fft.win.SetToolTip(wx.ToolTip(eng_notation.num_to_str(self.frequency + (fRel*1e3))))

    # Mouse clicked on fft display - change frequency 
    def Click(self,event):
        fRel = ( event.GetX() - 330. ) / 14.266666
	if self.AM_mode == False:
           self.frequency = self.frequency + (fRel*1e3)
	else:
	   self.frequency = self.frequency + (fRel*1e3) - 7.5e3
        self.spin_ctrl_1.SetValue(int(self.frequency))
        self.slider_3.SetValue(int((self.frequency-self.f_slider_offset)/self.f_slider_scale))
        if self.AM_mode == False:
	   self.xlate.set_center_freq ( self.usrp_center - ( self.frequency - self.tune_offset ))
	else:
	   self.xlate.set_center_freq( self.usrp_center - (self.frequency - self.tune_offset - 7.5e3))

    # Set power of AM sync carrier
    def am_carrier(self,event):
	scale = math.pow(10,(self.slider_6.GetValue())/50.)
	self.pll_carrier_scale.set_k(complex(scale,0))

    # Reset AT data and start calibrate over
    def AT_reset(self, event):
        self.xdata = []
        self.ydata = []

    # Save AT setting for a particular frequency
    def AT_calibrate(self, event):
        self.xdata.append(float(self.frequency))
        self.ydata.append(self.slider_7.GetValue())
        if len(self.xdata) > 1:
          self.m = []
          self.b = []
          for i in range(0,len(self.xdata)-1):
            self.m.append( (self.ydata[i+1] - self.ydata[i]) / (self.xdata[i+1] - self.xdata[i]) )
            self.b.append( self.ydata[i] - self.m[i] * self.xdata[i] )

    # Lookup calibrated points and calculate interpolated antenna tune voltage.
    # This is to automatically tune a narrowband loop antenna when the freq
    # is changed, to keep signals peaked.
    def auto_antenna_tune(self):
        for i in range(0,len(self.xdata)-1):
          if (self.frequency > self.xdata[i]) & (self.frequency < self.xdata[i+1]):
            self.slider_7.SetValue(self.m[i]*self.frequency + self.b[i])
        self.antenna_tune(0)

    # Slider to set loop antenna capacitance
    def antenna_tune(self, evt):
        if self.PLAY_FROM_USRP:
           self.src.write_aux_dac(0,3,self.slider_7.GetValue())

    # Timer events - check for web commands
    def OnUpdate(self):
      cmds = os.listdir("/var/www/cgi-bin/commands/")
      if cmds!=[]:
        if cmds[0]=='chfreq':
          fd=open("/var/www/cgi-bin/commands/chfreq","r")
          new=fd.readline()
          fd.close()
          if new!='':
            os.unlink("/var/www/cgi-bin/commands/chfreq")
            if ( int(new) >= self.f_lo ) & ( int(new) <= self.f_hi ):
               self.frequency = int(new)
               self.slider_3.SetValue(( self.frequency - self.f_slider_offset) / self.f_slider_scale )
               self.spin_ctrl_1.SetValue(self.frequency)
               if self.button_12.GetValue():
                 self.auto_antenna_tune()
               if self.AM_mode:
                 self.xlate.set_center_freq ( self.usrp_center - ( self.frequency - self.tune_offset - 7.5e3 ))
               else:
                 self.xlate.set_center_freq ( self.usrp_center - ( self.frequency - self.tune_offset ))

        if cmds[0]=='chvolume':
          fd=open("/var/www/cgi-bin/commands/chvolume","r")
          new=fd.readline()
          fd.close()
          if new!='':
            os.unlink("/var/www/cgi-bin/commands/chvolume")
            if ( int(new) >= 0 ) & ( int(new) <= 500 ):
               self.volume = int(new)
               self.slider_4.SetValue(self.volume)
               self.scale.set_k(math.pow(10.,((self.slider_4.GetValue()-500.)/100.)))

      else: # no new web commands, update state
        fh = open("/var/www/cgi-bin/state/freq","w")
        fh.write(str(int(self.frequency))+'\n')
        fh.close()
        fh = open("/var/www/cgi-bin/state/volume","w")
        fh.write(str(self.slider_4.GetValue())+'\n')
        fh.close()


# end of class MyFrame

# wx.Timer to check for web updates
class UpdateTimer(wx.Timer):
    def __init__(self, target, dur=1000):
        wx.Timer.__init__(self)
        self.target = target
        self.Start(dur)

    def Notify(self):
        """Called every timer interval"""
        if self.target:
            self.target.OnUpdate()


class MyApp(wx.App):
  def OnInit(self):
    frame = MyFrame(None, -1, "HF Explorer 2")
    frame.Show(True)
    self.SetTopWindow(frame)
    return True

app = MyApp(0)
app.MainLoop()

