summaryrefslogtreecommitdiff
path: root/gr-uhd/apps/hf_explorer
diff options
context:
space:
mode:
Diffstat (limited to 'gr-uhd/apps/hf_explorer')
-rw-r--r--gr-uhd/apps/hf_explorer/.gitignore3
-rw-r--r--gr-uhd/apps/hf_explorer/Makefile.am31
-rw-r--r--gr-uhd/apps/hf_explorer/README42
-rwxr-xr-xgr-uhd/apps/hf_explorer/hfx.py823
-rw-r--r--gr-uhd/apps/hf_explorer/hfx_help180
5 files changed, 1079 insertions, 0 deletions
diff --git a/gr-uhd/apps/hf_explorer/.gitignore b/gr-uhd/apps/hf_explorer/.gitignore
new file mode 100644
index 000000000..b6950912c
--- /dev/null
+++ b/gr-uhd/apps/hf_explorer/.gitignore
@@ -0,0 +1,3 @@
+/Makefile
+/Makefile.in
+/*.pyc
diff --git a/gr-uhd/apps/hf_explorer/Makefile.am b/gr-uhd/apps/hf_explorer/Makefile.am
new file mode 100644
index 000000000..c8e7ecb25
--- /dev/null
+++ b/gr-uhd/apps/hf_explorer/Makefile.am
@@ -0,0 +1,31 @@
+#
+# Copyright 2006,2009 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.
+#
+
+include $(top_srcdir)/Makefile.common
+
+ourdatadir = $(exampledir)/hf_explorer
+
+dist_ourdata_DATA = \
+ README \
+ hfx_help
+
+dist_ourdata_SCRIPTS = \
+ hfx.py
diff --git a/gr-uhd/apps/hf_explorer/README b/gr-uhd/apps/hf_explorer/README
new file mode 100644
index 000000000..57f45ceba
--- /dev/null
+++ b/gr-uhd/apps/hf_explorer/README
@@ -0,0 +1,42 @@
+hfx.py is meant to be a full-featured Long Wave / Medium Wave
+and Short Wave (250kHz to 30Mhz) AM and Single Sideband receiver.
+It uses the USRP with a Basic RX board, and will need an
+antenna and some preamps, about 30db gain will work. See the
+'Help' menu or hfx_help for more info.
+
+----------------------------------------------------------
+
+Powermate knob supported but not required, tooltip frequency display,
+single click tuning, AGC, record to disk, play from disk and record
+audio. Ability to tailor the audio passband with two sliders over the
+spectrum display. The sliders almost align with the actual
+frequency. Preset filter settings for LSB (-3000 to 0kHz), USB (0 to
++3000kHz), CW (-400 to -800Hz) and AM (-5kHz from carrier to +5kHz).
+
+AM now switches in a synchronous PLL detector with the carriers at
+7.5kHz. The PLL carrier is displayed in the bottom display and helps
+show where on the upper spectrum the demodulated signal
+lies. Everything gets shifted up 7.5kHz in AM, center frequency,
+tooltips, etc. The target AM carrier needs to be closely tuned in, it
+will have a hollow sound untill it is locked, and then the PLL carrier
+in the bottom display will jump up and remain relatively
+constant. There is a slider "AM sync carrier" to play with different
+levels to mix with the signal for demodulation. The filter in AM is
+preset to 2500/12500 (7.5kHz +/- 5kHz) and is handy for removing
+adjacent channel interference. Change AM_SYNC_DISPLAY in script for
+whether to show AM Sync carrier or not.
+
+Run with "-h" for command line help with setting USRP ddc center
+frequency, decimation, rf data record, playback and audio data
+recording.
+
+There are some controls for controlling a varactor and tuning an
+antenna - just ignore them unless you want to build a voltage tuned
+antenna to track frequency.
+
+There is also code for Web based control of frequency and volume - so
+I can tune the radio with an Ipaq from bed. Disabled by default - it
+takes a web server, some directories and scripts to use.
+
+
+
diff --git a/gr-uhd/apps/hf_explorer/hfx.py b/gr-uhd/apps/hf_explorer/hfx.py
new file mode 100755
index 000000000..687adf82b
--- /dev/null
+++ b/gr-uhd/apps/hf_explorer/hfx.py
@@ -0,0 +1,823 @@
+#!/usr/bin/env python
+# generated by wxGlade 0.4 on Tue Mar 14 10:16:06 2006
+#
+# Copyright 2006,2011 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
+from gnuradio.eng_option import eng_option
+from gnuradio import uhd
+from optparse import OptionParser
+
+n2s = eng_notation.num_to_str
+
+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
+
+
+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_fcutoff_hi = wx.Slider(self, ID_SLIDER_1, 0, -15798, 15799,
+ style=wx.SL_HORIZONTAL|wx.SL_LABELS)
+ self.button_6 = wx.ToggleButton(self, ID_BUTTON_6, "Lower")
+ self.slider_fcutoff_lo = wx.Slider(self, ID_SLIDER_2, 0, -15799, 15798,
+ 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("", "--address", type="string", default="addr=192.168.10.2",
+ help="Address of UHD device, [default=%default]")
+ parser.add_option ("-c", "--ddc-freq", type="eng_float", default=3.9e6,
+ help="set Rx DDC frequency to FREQ", metavar="FREQ")
+ parser.add_option ("-s", "--samp-rate", type="eng_float", default=256e3,
+ help="set sample rate (bandwidth) [default=%default]")
+ 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 ("-O", "--audio-output", type="string", default="",
+ help="audio output device name. E.g., hw:0,0, /dev/dsp, or pulse")
+
+ (options, args) = parser.parse_args ()
+
+ self.usrp_center = options.ddc_freq
+ input_rate = options.samp_rate
+ self.slider_range = input_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 (input_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
+ 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 = uhd.usrp_source(device_addr=options.address,
+ io_type=uhd.io_type.COMPLEX_FLOAT32,
+ num_channels=1)
+ self.src.set_samp_rate(input_rate)
+ input_rate = self.src.get_samp_rate()
+
+ self.src.set_center_freq(self.usrp_center, 0)
+ self.tune_offset = 0
+
+ else:
+ self.src = gr.file_source (gr.sizeof_short,options.input_file)
+ self.tune_offset = 2200 # 2200 works for 3.5-4Mhz band
+
+ # 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))
+
+ # save radio data to a file
+ if SAVE_RADIO_TO_FILE:
+ radio_file = gr.file_sink(gr.sizeof_short, options.radio_file)
+ self.tb.connect (self.src, radio_file)
+
+ # 2nd DDC
+ xlate_taps = gr.firdes.low_pass ( \
+ 1.0, input_rate, 16e3, 4e3, gr.firdes.WIN_HAMMING )
+ self.xlate = gr.freq_xlating_fir_filter_ccf ( \
+ fir_decim, xlate_taps, self.tune_offset, input_rate )
+
+ # 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_fcutoff_hi.SetValue(0)
+ self.slider_fcutoff_lo.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),
+ options.audio_output)
+
+
+ if self.PLAY_FROM_USRP:
+ self.tb.connect(self.src, self.xlate, self.fft)
+ else:
+ 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
+ self.fft.win.Bind(wx.EVT_LEFT_UP, self.Mouse)
+ # and left click to re-tune
+ self.fft.win.Bind(wx.EVT_LEFT_DOWN, self.Click)
+
+ # 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")
+ self.slider_fcutoff_hi.SetMinSize((450, 38))
+ self.slider_fcutoff_lo.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_fcutoff_hi, 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_fcutoff_lo, 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_fcutoff_hi.SetValue(self.slider_fcutoff_hi.GetValue()+event.delta)
+ if self.slider_fcutoff_lo.GetValue() > (self.slider_fcutoff_hi.GetValue() - 200) :
+ self.slider_fcutoff_lo.SetValue(self.slider_fcutoff_hi.GetValue() - 200)
+ self.filter()
+ if self.active_button == 6:
+ self.slider_fcutoff_lo.SetValue(self.slider_fcutoff_lo.GetValue()+event.delta)
+ if self.slider_fcutoff_hi.GetValue() < (self.slider_fcutoff_lo.GetValue() + 200) :
+ self.slider_fcutoff_hi.SetValue(self.slider_fcutoff_lo.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_fcutoff_hi.GetValue()
+ slider2 = self.slider_fcutoff_lo.GetValue()
+ if slider == ID_SLIDER_1:
+ if slider2 > (self.slider_fcutoff_hi.GetValue() - 200) :
+ self.slider_fcutoff_lo.SetValue(slider1 - 200)
+ elif slider == ID_SLIDER_2:
+ if slider1 < (self.slider_fcutoff_lo.GetValue() + 200) :
+ self.slider_fcutoff_hi.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_fcutoff_lo.GetValue(), # low cutoff
+ self.slider_fcutoff_hi.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_fcutoff_hi.SetValue(0)
+ self.slider_fcutoff_lo.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_fcutoff_hi.SetValue(3000)
+ self.slider_fcutoff_lo.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_fcutoff_hi.SetValue(12500)
+ self.slider_fcutoff_lo.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_fcutoff_hi.SetValue(-400)
+ self.slider_fcutoff_lo.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.src.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:
+ dev = self.src.get_dboard_iface()
+ dev.write_aux_dac(uhd.dboard_iface.UNIT_RX,
+ uhd.dboard_iface.AUX_DAC_C,
+ float(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")
+ frame.Show(True)
+ self.SetTopWindow(frame)
+ return True
+
+
+if __name__ == "__main__":
+ app = MyApp(0)
+ app.MainLoop()
+
diff --git a/gr-uhd/apps/hf_explorer/hfx_help b/gr-uhd/apps/hf_explorer/hfx_help
new file mode 100644
index 000000000..9a52dd2bb
--- /dev/null
+++ b/gr-uhd/apps/hf_explorer/hfx_help
@@ -0,0 +1,180 @@
+
+ HF Explorer Help
+
+ -----------------------------------------------------------------------
+
+ Command Line Switches:
+
+ -c DDC center frequency, set band.
+ -c 7.2e6 or -c 7.2M for 40 meter ham band.
+ Default is 3.9e6 80 meter ham band.
+ Example:
+
+ hfx.py -c 9500k
+
+ starts up in the 31 meter band.
+
+ -a Audio output file. Output file for 32kHz two channel
+ signed word audio. Two channels are used for
+ independent sideband. This file can be converted
+ to a wav file with sox. Example:
+
+ sox -c 2 -r 3200 file.sw file.wav
+
+ sox needs the .sw extension to indicate file type.
+ If not specified no audio file is created.
+
+ -r Radio output file. File to write RF data to for later
+ demodulation. Records the entire band to disk, width
+ determined by sample rate/decimation. Be sure to
+ note the decimation and center freq for later use!
+ Example:
+
+ hfx.py -c 900e3 -d 80 -r rf_data_AM-c900e3-d80
+
+ writes a pre-demod rf file centered on 900kHz with a
+ bandwidth of 800kHz (That's 80 AM stations!). The
+ center and decimation could be put in the filename for
+ proper use later.
+ If not specified no rf data file is created.
+ At default 250 decimation disk usage is about
+ 8Gb / hour.
+
+ -i Radio input file. Use to play back a previously
+ recorded rf data file. Example:
+
+ hfx.py -c 900e3 -d 80 -i rf_data_AM-c900e3-d80
+
+ plays back the previously recorded band, no
+ usrp hardware needed. Tune about the 800kHz wide band.
+ When playing a recorded file, time controls
+ fast-forward and rewind are available.
+
+ -d Decimation. Sets sample rate and bandwidth.
+ This is the factor that the usrp sample rate, 64e6,
+ is divided by. Default is 250 for 256kHz bandwidth
+ which is enough to record a ham band without
+ eating up disk space too fast. The 64e6 sample
+ rate limits the upper practical frequency to 32MHz.
+ The Basic RX transformer limits the lower frequency
+ to about 200kHz.
+
+
+ Powermate Knob:
+
+ A Powermate knob is recommended but not necessary. If a knob
+ is used, it is in one of 3 or 4 modes controlling frequency,
+ volume, filter and (if playing a recorded file) time.
+ Pushing the knob switches mode and the buttons on the HFX panel
+ change to show which mode is in effect. Then just turn the knob
+ to set frequency, volume, filter or go fast forward or rewind.
+
+
+ Bandswitch:
+
+ Across the top are a set of predefined bands and buttons
+ to rapidly switch to the center of that band. To change a band,
+ type the frequency in Hz into the box under "Center Frequency",
+ then press "Set" on the left, then the button you want to
+ program. From then on (untill the program is exited) pushing
+ that button takes you to that band. To make a band button
+ permenant edit the hfx.py script with whatever frequency you
+ want assigned to what button.
+
+
+ Frequency:
+
+ There are 6 ways to set the frequency.
+ 1) Move the slider with the mouse
+ 2) Use the Spin Control up/down arrows (very fine 1Hz control)
+ 3) Type the frequency in Hz into the Spin Control
+ 4) Turn the Powermate knob
+ 5) Web control.
+ 6) Clicking on the FFT display to set demod center. This is very
+ convenient for tuning +-15kHz when you see a signal on the
+ display. If in Lower Sideband, clicking just to the right of
+ a signal will tune to it immediately. Clicking several times
+ on the far right right or left of the display will rapidly
+ tune up or down the band.
+
+
+ Volume:
+
+ Move the volume slider with the mouse, or push the Powermate knob
+ untill the volume button is active, or click on the volume button,
+ then turn the knob. Volume can also be set by web control if web
+ control is setup and enabled.
+
+
+ Filter:
+
+ Similar to volume, switches in any of 30 audio filters from 600
+ to 3600Hz in Sideband or up to 5kHz in AM.
+
+
+ Mode:
+
+ Demodulation modes are chosen by clicking on the buttons for
+ Lower Sideband, Upper Sideband, or AM.
+
+
+ PGA:
+
+ PGA slider sets the rf gain in the Analog-to-Digital converter
+ before digitizing. 0 to 20db gain easily shows up on the FFT
+ display.
+
+
+ Time:
+
+ When playing back a recorded RF data file, you can enjoy the
+ freedom of rewinding or fast-forwarding. Replay a weak signal
+ or skip through annoying AM commercials.
+
+
+ Antennas and Preamps:
+
+ The USRP Basic RX board is not sensitive enough for anything but
+ the strongest signals. In my experience about 40 db of small
+ signal gain is required to make the HFX as sensitive as other
+ receivers. Some working amplifiers are the Ramsey PR-2 with 20db
+ gain, fairly low noise and more bandwidth than we can use here.
+ Also the amp modules from Advanced Receiver Research are nice.
+ I use an ARR 7-7.4MHz GaAsFET 29db amp with .5db noise at the
+ apex of a 40 meter dipole with excellent results. Another
+ amp I like is a Minicircuits ZHL-32A 29db amp but they are
+ expensive and hard to find. Also it may help to use some filters
+ to keep strong local signals from the ADC, or limit rf input
+ to the band of interest, etc.
+ Resonant outdoor antennas, like a dipole, in a low-noise (away
+ from consumer electronics) environment are nice. Long random wires
+ with a tuner work. I like a small indoor tuned loop made from 10ft
+ of 1/4" copper tube, a 365pf tuning cap and a pickup loop connected
+ to rg-58.
+
+
+ Web Control:
+
+ To control your radio remotely, ensure you have a web server
+ (Apache, etc) working and a compatible directory structure in
+ place. Directories /var/www/cgi-bin/commands and
+ /var/www/cgi-bin/state must already exist. You will need a
+ home page with forms and a set of scripts to put commands in
+ and put the current state on the home page. email me for further
+ help. Setting WEB_CONTROL to True in hfx.py turns on the timers
+ that check for commands and update the state.
+
+
+ IF Output:
+
+ There is a provision for outputting un-demodulated complex
+ through the audio out in stereo for use with Digital Radio
+ Mondial (DRM) or using a seperate demodulation program like
+ SDRadio (by I2PHD).
+ Set IF_OUTPUT to True in weaver_isb_am1_usrp4.py.
+
+
+ --Good luck and happy LW/MW/SW Exploring.
+ Chuck
+ chuckek@musicriver.homeunix.com
+