2 # -*- coding: ANSI_X3.4-1968 -*-
3 # generated by wxGlade 0.4 on Tue Mar 14 10:16:06 2006
5 # Copyright 2006 Free Software Foundation, Inc.
7 # This file is part of GNU Radio
9 # GNU Radio is free software; you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation; either version 3, or (at your option)
14 # GNU Radio is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 # GNU General Public License for more details.
19 # You should have received a copy of the GNU General Public License
20 # along with GNU Radio; see the file COPYING. If not, write to
21 # the Free Software Foundation, Inc., 51 Franklin Street,
22 # Boston, MA 02110-1301, USA.
24 #-----------------------------------------------------------------
28 # (src)->(xlate)--+->(audio filter)--+-->(sel_am)-+--------------+
32 # | (pll_carrier_scale) |
34 # | (pll_carrier_filter) |
40 # | (phaser1) (phaser2) |
44 # V +---------->(am_det)
48 # +-->(sel_sb)------------>(combine)
51 # +--------------------------(scale)
56 # (agc)<--(offset)<--(intr)<---(sqr1)
61 #----------------------------------------------------------------------
63 # Versions 2.2.1 adds loop antenna automatic tuner
65 # 2.3.1 adds web control, made AM Sync display optional,
66 # added more comments.
68 # 2.4.1 updates usrp interface to support auto subdev
70 # Web server control disabled by default. Do not enable
71 # until directory structure and scripts are in place.
74 # Controls display of AM Sync Carrier - turn off for smaller
75 # window if not needed
76 AM_SYNC_DISPLAY = False
78 import os, wx, sys, math
79 import wx.lib.evtmgr as em
80 from gnuradio.wxgui import powermate, fftsink
81 from gnuradio import gr, audio, eng_notation, usrp, gru
82 from gnuradio.eng_option import eng_option
83 from optparse import OptionParser
85 ID_BUTTON_1 = wx.NewId() # LSB button
86 ID_BUTTON_2 = wx.NewId() # USB
87 ID_BUTTON_3 = wx.NewId() # AM
88 ID_BUTTON_4 = wx.NewId() # CW
89 ID_BUTTON_5 = wx.NewId() # Powermate controls: Upper audio freq cutoff
90 ID_BUTTON_6 = wx.NewId() # " Lower audio freq cutoff
91 ID_BUTTON_7 = wx.NewId() # " Frequency
92 ID_BUTTON_8 = wx.NewId() # " Volume
93 ID_BUTTON_9 = wx.NewId() # " Time
94 ID_BUTTON_10 = wx.NewId() # Time Seek Forwards
95 ID_BUTTON_11 = wx.NewId() # Time Seek Backwards
96 ID_BUTTON_12 = wx.NewId() # Automatic Antenna Tune (AT) enable
97 ID_BUTTON_13 = wx.NewId() # AT Calibrate point
98 ID_BUTTON_14 = wx.NewId() # AT Reset
99 ID_TEXT_1 = wx.NewId() # Band Center, USRP ddc Freq
100 ID_SPIN_1 = wx.NewId() # Frequency display and control
101 ID_SLIDER_1 = wx.NewId() # Upper audio freq cutoff
102 ID_SLIDER_2 = wx.NewId() # Lower audio freq cutoff
103 ID_SLIDER_3 = wx.NewId() # Frequency
104 ID_SLIDER_4 = wx.NewId() # Volume
105 ID_SLIDER_5 = wx.NewId() # Programmable Gain Amp, PGA, RF gain
106 ID_SLIDER_6 = wx.NewId() # AM Sync carrier level
107 ID_SLIDER_7 = wx.NewId() # AT control voltage output
108 ID_EXIT = wx.NewId() # Menu Exit
111 def pick_subdevice(u):
113 The user didn't specify a subdevice on the command line.
114 If there's a daughterboard on A, select A.
115 If there's a daughterboard on B, select B.
118 if u.db[0][0].dbid() >= 0: # dbid is < 0 if there's no d'board or a problem
120 if u.db[1][0].dbid() >= 0:
125 class MyFrame(wx.Frame):
126 def __init__(self, *args, **kwds):
127 # begin wxGlade: MyFrame.__init__
128 kwds["style"] = wx.DEFAULT_FRAME_STYLE
129 wx.Frame.__init__(self, *args, **kwds)
132 self.frame_1_menubar = wx.MenuBar()
133 self.SetMenuBar(self.frame_1_menubar)
134 wxglade_tmp_menu = wx.Menu()
135 self.Exit = wx.MenuItem(wxglade_tmp_menu, ID_EXIT, "Exit", "Exit", wx.ITEM_NORMAL)
136 wxglade_tmp_menu.AppendItem(self.Exit)
137 self.frame_1_menubar.Append(wxglade_tmp_menu, "File")
139 self.panel_1 = wx.Panel(self, -1)
140 self.button_1 = wx.Button(self, ID_BUTTON_1, "LSB")
141 self.button_2 = wx.Button(self, ID_BUTTON_2, "USB")
142 self.button_3 = wx.Button(self, ID_BUTTON_3, "AM")
143 self.button_4 = wx.Button(self, ID_BUTTON_4, "CW")
144 self.button_5 = wx.ToggleButton(self, ID_BUTTON_5, "Upper")
145 self.slider_1 = wx.Slider(self, ID_SLIDER_1, 0, -15799, 15799, style=wx.SL_HORIZONTAL|wx.SL_LABELS)
146 self.button_6 = wx.ToggleButton(self, ID_BUTTON_6, "Lower")
147 self.slider_2 = wx.Slider(self, ID_SLIDER_2, 0, -15799, 15799, style=wx.SL_HORIZONTAL|wx.SL_LABELS)
148 self.panel_5 = wx.Panel(self, -1)
149 self.label_1 = wx.StaticText(self, -1, " Band\nCenter")
150 self.text_ctrl_1 = wx.TextCtrl(self, ID_TEXT_1, "")
151 self.panel_6 = wx.Panel(self, -1)
152 self.panel_7 = wx.Panel(self, -1)
153 self.panel_2 = wx.Panel(self, -1)
154 self.button_7 = wx.ToggleButton(self, ID_BUTTON_7, "Freq")
155 self.slider_3 = wx.Slider(self, ID_SLIDER_3, 3000, 0, 6000)
156 self.spin_ctrl_1 = wx.SpinCtrl(self, ID_SPIN_1, "", min=0, max=100)
157 self.button_8 = wx.ToggleButton(self, ID_BUTTON_8, "Vol")
158 self.slider_4 = wx.Slider(self, ID_SLIDER_4, 0, 0, 500)
159 self.slider_5 = wx.Slider(self, ID_SLIDER_5, 0, 0, 20)
160 self.button_9 = wx.ToggleButton(self, ID_BUTTON_9, "Time")
161 self.button_11 = wx.Button(self, ID_BUTTON_11, "Rew")
162 self.button_10 = wx.Button(self, ID_BUTTON_10, "Fwd")
163 self.panel_3 = wx.Panel(self, -1)
164 self.label_2 = wx.StaticText(self, -1, "PGA ")
165 self.panel_4 = wx.Panel(self, -1)
166 self.panel_8 = wx.Panel(self, -1)
167 self.panel_9 = wx.Panel(self, -1)
168 self.label_3 = wx.StaticText(self, -1, "AM Sync\nCarrier")
169 self.slider_6 = wx.Slider(self, ID_SLIDER_6, 50, 0, 200, style=wx.SL_HORIZONTAL|wx.SL_LABELS)
170 self.label_4 = wx.StaticText(self, -1, "Antenna Tune")
171 self.slider_7 = wx.Slider(self, ID_SLIDER_7, 1575, 950, 2200, style=wx.SL_HORIZONTAL|wx.SL_LABELS)
172 self.panel_10 = wx.Panel(self, -1)
173 self.button_12 = wx.ToggleButton(self, ID_BUTTON_12, "Auto Tune")
174 self.button_13 = wx.Button(self, ID_BUTTON_13, "Calibrate")
175 self.button_14 = wx.Button(self, ID_BUTTON_14, "Reset")
176 self.panel_11 = wx.Panel(self, -1)
177 self.panel_12 = wx.Panel(self, -1)
179 self.__set_properties()
183 parser = OptionParser (option_class=eng_option)
184 parser.add_option ("-c", "--ddc-freq", type="eng_float", default=3.9e6,
185 help="set Rx DDC frequency to FREQ", metavar="FREQ")
186 parser.add_option ("-a", "--audio_file", default="",
187 help="audio output file", metavar="FILE")
188 parser.add_option ("-r", "--radio_file", default="",
189 help="radio output file", metavar="FILE")
190 parser.add_option ("-i", "--input_file", default="",
191 help="radio input file", metavar="FILE")
192 parser.add_option ("-d", "--decim", type="int", default=250,
193 help="USRP decimation")
194 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)")
195 (options, args) = parser.parse_args ()
197 self.usrp_center = options.ddc_freq
198 usb_rate = 64e6 / options.decim
199 self.slider_range = usb_rate * 0.9375
200 self.f_lo = self.usrp_center - (self.slider_range/2)
201 self.f_hi = self.usrp_center + (self.slider_range/2)
202 self.af_sample_rate = 32000
203 fir_decim = long (usb_rate / self.af_sample_rate)
205 # data point arrays for antenna tuner
209 self.fg = gr.flow_graph()
211 # radio variables, initial conditions
212 self.frequency = self.usrp_center
213 # these map the frequency slider (0-6000) to the actual range
214 self.f_slider_offset = self.f_lo
215 self.f_slider_scale = 10000/options.decim
216 self.spin_ctrl_1.SetRange(self.f_lo,self.f_hi)
217 self.text_ctrl_1.SetValue(str(int(self.usrp_center)))
218 self.slider_5.SetValue(0)
221 self.slider_3.SetValue((self.frequency-self.f_slider_offset)/self.f_slider_scale)
222 self.spin_ctrl_1.SetValue(int(self.frequency))
226 self.pm = powermate.powermate(self)
228 sys.stderr.write("Unable to find PowerMate or Contour Shuttle\n")
232 powermate.EVT_POWERMATE_ROTATE(self, self.on_rotate)
233 powermate.EVT_POWERMATE_BUTTON(self, self.on_pmButton)
234 self.active_button = 7
236 # command line options
237 if options.audio_file == "": SAVE_AUDIO_TO_FILE = False
238 else: SAVE_AUDIO_TO_FILE = True
239 if options.radio_file == "": SAVE_RADIO_TO_FILE = False
240 else: SAVE_RADIO_TO_FILE = True
241 if options.input_file == "": self.PLAY_FROM_USRP = True
242 else: self.PLAY_FROM_USRP = False
244 if self.PLAY_FROM_USRP:
245 self.src = usrp.source_c(decim_rate=options.decim)
246 if options.rx_subdev_spec is None:
247 options.rx_subdev_spec = pick_subdevice(self.src)
248 self.src.set_mux(usrp.determine_rx_mux_value(self.src, options.rx_subdev_spec))
249 self.subdev = usrp.selected_subdev(self.src, options.rx_subdev_spec)
250 self.src.tune(0, self.subdev, self.usrp_center)
251 self.tune_offset = 0 # -self.usrp_center - self.src.rx_freq(0)
254 self.src = gr.file_source (gr.sizeof_gr_complex,options.input_file)
255 self.tune_offset = 2200 # 2200 works for 3.5-4Mhz band
257 # save radio data to a file
258 if SAVE_RADIO_TO_FILE:
259 file = gr.file_sink(gr.sizeof_gr_complex, options.radio_file)
260 self.fg.connect (self.src, file)
263 xlate_taps = gr.firdes.low_pass ( \
264 1.0, usb_rate, 16e3, 4e3, gr.firdes.WIN_HAMMING )
265 self.xlate = gr.freq_xlating_fir_filter_ccf ( \
266 fir_decim, xlate_taps, self.tune_offset, usb_rate )
268 # Complex Audio filter
269 audio_coeffs = gr.firdes.complex_band_pass (
271 self.af_sample_rate, # sample rate
275 gr.firdes.WIN_HAMMING) # window
276 self.slider_1.SetValue(0)
277 self.slider_2.SetValue(-3000)
279 self.audio_filter = gr.fir_filter_ccc ( 1, audio_coeffs)
281 # Main +/- 16Khz spectrum display
282 self.fft = fftsink.fft_sink_c (self.fg, self.panel_2, fft_size=512, sample_rate=self.af_sample_rate, average=True, size=(640,240))
286 self.fft2 = fftsink.fft_sink_c (self.fg, self.panel_9, y_per_div=20, fft_size=512, sample_rate=self.af_sample_rate, average=True, size=(640,240))
288 c2f = gr.complex_to_float()
291 self.sel_am = gr.multiply_const_cc(0)
292 # the following frequencies turn out to be in radians/sample
293 # gr.pll_refout_cc(alpha,beta,min_freq,max_freq)
294 # suggested alpha = X, beta = .25 * X * X
295 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))
296 self.pll_carrier_scale = gr.multiply_const_cc(complex(10,0))
297 am_det = gr.multiply_cc()
298 # these are for converting +7.5kHz to -7.5kHz
299 # for some reason gr.conjugate_cc() adds noise ??
300 c2f2 = gr.complex_to_float()
301 c2f3 = gr.complex_to_float()
302 f2c = gr.float_to_complex()
303 phaser1 = gr.multiply_const_ff(1)
304 phaser2 = gr.multiply_const_ff(-1)
306 # filter for pll generated carrier
307 pll_carrier_coeffs = gr.firdes.complex_band_pass (
309 self.af_sample_rate, # sample rate
313 gr.firdes.WIN_HAMMING) # window
315 self.pll_carrier_filter = gr.fir_filter_ccc ( 1, pll_carrier_coeffs)
317 self.sel_sb = gr.multiply_const_ff(1)
318 combine = gr.add_ff()
321 sqr1 = gr.multiply_ff()
322 intr = gr.iir_filter_ffd ( [.004, 0], [0, .999] )
323 offset = gr.add_const_ff(1)
327 self.scale = gr.multiply_const_ff(0.00001)
328 dst = audio.sink(long(self.af_sample_rate))
330 self.fg.connect(self.src,self.xlate,self.fft)
331 self.fg.connect(self.xlate,self.audio_filter,self.sel_am,(am_det,0))
332 self.fg.connect(self.sel_am,pll,self.pll_carrier_scale,self.pll_carrier_filter,c2f3)
333 self.fg.connect((c2f3,0),phaser1,(f2c,0))
334 self.fg.connect((c2f3,1),phaser2,(f2c,1))
335 self.fg.connect(f2c,(am_det,1))
336 self.fg.connect(am_det,c2f2,(combine,0))
337 self.fg.connect(self.audio_filter,c2f,self.sel_sb,(combine,1))
339 self.fg.connect(self.pll_carrier_filter,self.fft2)
340 self.fg.connect(combine,self.scale)
341 self.fg.connect(self.scale,(sqr1,0))
342 self.fg.connect(self.scale,(sqr1,1))
343 self.fg.connect(sqr1, intr, offset, (agc, 1))
344 self.fg.connect(self.scale,(agc, 0))
345 self.fg.connect(agc,dst)
347 if SAVE_AUDIO_TO_FILE:
348 f_out = gr.file_sink(gr.sizeof_short,options.audio_file)
349 sc1 = gr.multiply_const_ff(64000)
350 f2s1 = gr.float_to_short()
351 self.fg.connect(agc,sc1,f2s1,f_out)
355 # for mouse position reporting on fft display
356 em.eventManager.Register(self.Mouse, wx.EVT_MOTION, self.fft.win)
357 # and left click to re-tune
358 em.eventManager.Register(self.Click, wx.EVT_LEFT_DOWN, self.fft.win)
360 # start a timer to check for web commands
362 self.timer = UpdateTimer(self, 1000) # every 1000 mSec, 1 Sec
365 wx.EVT_BUTTON(self,ID_BUTTON_1,self.set_lsb)
366 wx.EVT_BUTTON(self,ID_BUTTON_2,self.set_usb)
367 wx.EVT_BUTTON(self,ID_BUTTON_3,self.set_am)
368 wx.EVT_BUTTON(self,ID_BUTTON_4,self.set_cw)
369 wx.EVT_BUTTON(self,ID_BUTTON_10,self.fwd)
370 wx.EVT_BUTTON(self,ID_BUTTON_11,self.rew)
371 wx.EVT_BUTTON(self, ID_BUTTON_13, self.AT_calibrate)
372 wx.EVT_BUTTON(self, ID_BUTTON_14, self.AT_reset)
373 wx.EVT_TOGGLEBUTTON(self,ID_BUTTON_5,self.on_button)
374 wx.EVT_TOGGLEBUTTON(self,ID_BUTTON_6,self.on_button)
375 wx.EVT_TOGGLEBUTTON(self,ID_BUTTON_7,self.on_button)
376 wx.EVT_TOGGLEBUTTON(self,ID_BUTTON_8,self.on_button)
377 wx.EVT_TOGGLEBUTTON(self,ID_BUTTON_9,self.on_button)
378 wx.EVT_SLIDER(self,ID_SLIDER_1,self.set_filter)
379 wx.EVT_SLIDER(self,ID_SLIDER_2,self.set_filter)
380 wx.EVT_SLIDER(self,ID_SLIDER_3,self.slide_tune)
381 wx.EVT_SLIDER(self,ID_SLIDER_4,self.set_volume)
382 wx.EVT_SLIDER(self,ID_SLIDER_5,self.set_pga)
383 wx.EVT_SLIDER(self,ID_SLIDER_6,self.am_carrier)
384 wx.EVT_SLIDER(self,ID_SLIDER_7,self.antenna_tune)
385 wx.EVT_SPINCTRL(self,ID_SPIN_1,self.spin_tune)
387 wx.EVT_MENU(self, ID_EXIT, self.TimeToQuit)
389 def __set_properties(self):
390 # begin wxGlade: MyFrame.__set_properties
391 self.SetTitle("HF Explorer 2")
392 self.slider_1.SetMinSize((450, 38))
393 self.slider_2.SetMinSize((450, 38))
394 self.panel_2.SetMinSize((640, 240))
395 self.button_7.SetValue(1)
396 self.slider_3.SetMinSize((450, 19))
397 self.slider_4.SetMinSize((275, 19))
398 self.slider_5.SetMinSize((275, 19))
400 self.panel_9.SetMinSize((640, 240))
401 self.slider_6.SetMinSize((300, 38))
402 self.slider_7.SetMinSize((400, 38))
405 def __do_layout(self):
406 # begin wxGlade: MyFrame.__do_layout
407 sizer_1 = wx.BoxSizer(wx.VERTICAL)
408 grid_sizer_1 = wx.FlexGridSizer(11, 2, 0, 0)
409 sizer_7 = wx.BoxSizer(wx.HORIZONTAL)
410 sizer_5 = wx.BoxSizer(wx.HORIZONTAL)
411 sizer_4 = wx.BoxSizer(wx.HORIZONTAL)
412 sizer_3 = wx.BoxSizer(wx.HORIZONTAL)
413 sizer_6 = wx.BoxSizer(wx.VERTICAL)
414 sizer_2 = wx.BoxSizer(wx.HORIZONTAL)
415 grid_sizer_1.Add(self.panel_1, 1, wx.EXPAND, 0)
416 sizer_2.Add(self.button_1, 0, wx.ADJUST_MINSIZE, 0)
417 sizer_2.Add(self.button_2, 0, wx.ADJUST_MINSIZE, 0)
418 sizer_2.Add(self.button_3, 0, wx.ADJUST_MINSIZE, 0)
419 sizer_2.Add(self.button_4, 0, wx.ADJUST_MINSIZE, 0)
420 grid_sizer_1.Add(sizer_2, 1, wx.EXPAND, 0)
421 grid_sizer_1.Add(self.button_5, 0, wx.ADJUST_MINSIZE, 0)
422 grid_sizer_1.Add(self.slider_1, 0, wx.ALIGN_CENTER_HORIZONTAL|wx.ADJUST_MINSIZE, 0)
423 grid_sizer_1.Add(self.button_6, 0, wx.ADJUST_MINSIZE, 0)
424 grid_sizer_1.Add(self.slider_2, 0, wx.ALIGN_CENTER_HORIZONTAL|wx.ADJUST_MINSIZE, 0)
425 sizer_6.Add(self.panel_5, 1, wx.EXPAND, 0)
426 sizer_6.Add(self.label_1, 0, wx.ALIGN_CENTER_HORIZONTAL|wx.ADJUST_MINSIZE, 0)
427 sizer_6.Add(self.text_ctrl_1, 0, wx.ADJUST_MINSIZE, 0)
428 sizer_6.Add(self.panel_6, 1, wx.EXPAND, 0)
429 sizer_6.Add(self.panel_7, 1, wx.EXPAND, 0)
430 grid_sizer_1.Add(sizer_6, 1, wx.EXPAND, 0)
431 grid_sizer_1.Add(self.panel_2, 1, wx.EXPAND, 0)
432 grid_sizer_1.Add(self.button_7, 0, wx.ADJUST_MINSIZE, 0)
433 sizer_3.Add(self.slider_3, 0, wx.ADJUST_MINSIZE, 0)
434 sizer_3.Add(self.spin_ctrl_1, 0, wx.ADJUST_MINSIZE, 0)
435 grid_sizer_1.Add(sizer_3, 1, wx.EXPAND, 0)
436 grid_sizer_1.Add(self.button_8, 0, wx.ADJUST_MINSIZE, 0)
437 sizer_4.Add(self.slider_4, 0, wx.ADJUST_MINSIZE, 0)
438 sizer_4.Add(self.slider_5, 0, wx.ADJUST_MINSIZE, 0)
439 grid_sizer_1.Add(sizer_4, 1, wx.EXPAND, 0)
440 grid_sizer_1.Add(self.button_9, 0, wx.ADJUST_MINSIZE, 0)
441 sizer_5.Add(self.button_11, 0, wx.ADJUST_MINSIZE, 0)
442 sizer_5.Add(self.button_10, 0, wx.ADJUST_MINSIZE, 0)
443 sizer_5.Add(self.panel_3, 1, wx.EXPAND, 0)
444 sizer_5.Add(self.label_2, 0, wx.ADJUST_MINSIZE, 0)
445 sizer_5.Add(self.panel_4, 1, wx.EXPAND, 0)
446 grid_sizer_1.Add(sizer_5, 1, wx.EXPAND, 0)
447 grid_sizer_1.Add(self.panel_8, 1, wx.EXPAND, 0)
448 grid_sizer_1.Add(self.panel_9, 1, wx.EXPAND, 0)
449 grid_sizer_1.Add(self.label_3, 0, wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 0)
450 grid_sizer_1.Add(self.slider_6, 0, wx.ADJUST_MINSIZE, 0)
451 grid_sizer_1.Add(self.label_4, 0, wx.ALIGN_BOTTOM|wx.ADJUST_MINSIZE, 0)
452 grid_sizer_1.Add(self.slider_7, 0, wx.ADJUST_MINSIZE, 0)
453 grid_sizer_1.Add(self.panel_10, 1, wx.EXPAND, 0)
454 sizer_7.Add(self.button_12, 0, wx.ADJUST_MINSIZE, 0)
455 sizer_7.Add(self.button_13, 0, wx.ADJUST_MINSIZE, 0)
456 sizer_7.Add(self.button_14, 0, wx.ADJUST_MINSIZE, 0)
457 sizer_7.Add(self.panel_11, 1, wx.EXPAND, 0)
458 sizer_7.Add(self.panel_12, 1, wx.EXPAND, 0)
459 grid_sizer_1.Add(sizer_7, 1, wx.EXPAND, 0)
460 sizer_1.Add(grid_sizer_1, 1, wx.EXPAND, 0)
461 self.SetAutoLayout(True)
462 self.SetSizer(sizer_1)
464 sizer_1.SetSizeHints(self)
469 def TimeToQuit(self, event):
473 # Powermate being turned
474 def on_rotate(self, event):
475 if self.active_button == 5:
476 self.slider_1.SetValue(self.slider_1.GetValue()+event.delta)
477 if self.slider_2.GetValue() > (self.slider_1.GetValue() - 200) :
478 self.slider_2.SetValue(self.slider_1.GetValue() - 200)
480 if self.active_button == 6:
481 self.slider_2.SetValue(self.slider_2.GetValue()+event.delta)
482 if self.slider_1.GetValue() < (self.slider_2.GetValue() + 200) :
483 self.slider_1.SetValue(self.slider_2.GetValue() + 200)
485 if self.active_button == 7:
486 new = max(0, min(6000, self.slider_3.GetValue() + event.delta))
487 self.slider_3.SetValue(new)
488 self.frequency = (self.f_slider_scale * new) + self.f_slider_offset
489 self.spin_ctrl_1.SetValue(self.frequency)
490 if self.AM_mode == False:
491 self.xlate.set_center_freq( self.usrp_center - (self.frequency - self.tune_offset))
493 self.xlate.set_center_freq( self.usrp_center - (self.frequency - self.tune_offset - 7.5e3))
494 if self.button_12.GetValue():
495 self.auto_antenna_tune()
496 if self.active_button == 8:
497 new = max(0, min(500, self.slider_4.GetValue() + event.delta))
498 self.slider_4.SetValue(new)
499 self.scale.set_k(math.pow(10.,((self.slider_4.GetValue()-500.)/100.)))
500 if self.active_button == 9:
501 if self.PLAY_FROM_USRP == False:
502 if event.delta == -1:
503 self.src.seek(-1000000,gr.SEEK_CUR)
504 elif event.delta == 1:
505 self.src.seek(1000000,gr.SEEK_CUR)
508 # Powermate pressed to switch controlled function
509 def on_pmButton(self, event):
511 if self.active_button == 5:
512 self.active_button = 6
513 self.button_5.SetValue(False)
514 self.button_6.SetValue(True)
515 elif self.active_button == 6:
516 self.active_button = 7
517 self.button_6.SetValue(False)
518 self.button_7.SetValue(True)
519 elif self.active_button == 7:
520 self.active_button = 8
521 self.button_7.SetValue(False)
522 self.button_8.SetValue(True)
523 elif self.active_button == 8:
524 self.active_button = 9
525 self.button_8.SetValue(False)
526 self.button_9.SetValue(True)
527 elif self.active_button == 9:
528 self.active_button = 5
529 self.button_9.SetValue(False)
530 self.button_5.SetValue(True)
532 # Clicking one PM control button turns the rest off
533 def on_button(self, event):
535 if id == ID_BUTTON_5:
536 self.active_button = 5
537 self.button_6.SetValue(False)
538 self.button_7.SetValue(False)
539 self.button_8.SetValue(False)
540 self.button_9.SetValue(False)
541 if id == ID_BUTTON_6:
542 self.active_button = 6
543 self.button_5.SetValue(False)
544 self.button_7.SetValue(False)
545 self.button_8.SetValue(False)
546 self.button_9.SetValue(False)
547 if id == ID_BUTTON_7:
548 self.active_button = 7
549 self.button_5.SetValue(False)
550 self.button_6.SetValue(False)
551 self.button_8.SetValue(False)
552 self.button_9.SetValue(False)
553 if id == ID_BUTTON_8:
554 self.active_button = 8
555 self.button_5.SetValue(False)
556 self.button_6.SetValue(False)
557 self.button_7.SetValue(False)
558 self.button_9.SetValue(False)
559 if id == ID_BUTTON_9:
560 self.active_button = 9
561 self.button_5.SetValue(False)
562 self.button_6.SetValue(False)
563 self.button_7.SetValue(False)
564 self.button_8.SetValue(False)
566 # Make sure filter settings are legal
567 def set_filter(self, event):
568 slider = event.GetId()
569 slider1 = self.slider_1.GetValue()
570 slider2 = self.slider_2.GetValue()
571 if slider == ID_SLIDER_1:
572 if slider2 > (self.slider_1.GetValue() - 200) :
573 self.slider_2.SetValue(slider1 - 200)
574 elif slider == ID_SLIDER_2:
575 if slider1 < (self.slider_2.GetValue() + 200) :
576 self.slider_1.SetValue(slider2 + 200)
579 # Calculate taps and apply
581 audio_coeffs = gr.firdes.complex_band_pass (
583 self.af_sample_rate, # sample rate
584 self.slider_2.GetValue(), # low cutoff
585 self.slider_1.GetValue(), # high cutoff
587 gr.firdes.WIN_HAMMING) # window
588 self.audio_filter.set_taps(audio_coeffs)
590 def set_lsb(self, event):
592 self.xlate.set_center_freq( self.usrp_center - (self.frequency - self.tune_offset))
595 self.slider_1.SetValue(0)
596 self.slider_2.SetValue(-3000)
599 def set_usb(self, event):
601 self.xlate.set_center_freq( self.usrp_center - (self.frequency - self.tune_offset))
604 self.slider_1.SetValue(3000)
605 self.slider_2.SetValue(0)
608 def set_am(self, event):
610 self.xlate.set_center_freq( self.usrp_center - (self.frequency - self.tune_offset - 7.5e3))
613 self.slider_1.SetValue(12500)
614 self.slider_2.SetValue(2500)
617 def set_cw(self, event):
619 self.xlate.set_center_freq( self.usrp_center - (self.frequency - self.tune_offset))
623 self.slider_1.SetValue(-400)
624 self.slider_2.SetValue(-800)
627 def set_volume(self, event):
628 self.scale.set_k(math.pow(10.,((self.slider_4.GetValue()-500.)/100.)))
630 def set_pga(self,event):
631 if self.PLAY_FROM_USRP:
632 self.subdev.set_gain(self.slider_5.GetValue())
634 def slide_tune(self, event):
635 self.frequency = (self.f_slider_scale * self.slider_3.GetValue()) + self.f_slider_offset
636 if self.AM_mode == False:
637 self.xlate.set_center_freq( self.usrp_center - (self.frequency - self.tune_offset))
639 self.xlate.set_center_freq( self.usrp_center - (self.frequency - self.tune_offset - 7.5e3))
640 self.spin_ctrl_1.SetValue(self.frequency)
641 if self.button_12.GetValue():
642 self.auto_antenna_tune()
644 def spin_tune(self, event):
645 self.frequency = self.spin_ctrl_1.GetValue()
646 if self.AM_mode == False:
647 self.xlate.set_center_freq( self.usrp_center - (self.frequency - self.tune_offset))
649 self.xlate.set_center_freq( self.usrp_center - (self.frequency - self.tune_offset - 7.5e3))
650 self.slider_3.SetValue(int((self.frequency-self.f_slider_offset)/self.f_slider_scale))
651 if self.button_12.GetValue():
652 self.auto_antenna_tune()
654 # Seek forwards in file
655 def fwd(self, event):
656 if self.PLAY_FROM_USRP == False:
657 self.src.seek(10000000,gr.SEEK_CUR)
659 # Seek backwards in file
660 def rew(self, event):
661 if self.PLAY_FROM_USRP == False:
662 self.src.seek(-10000000,gr.SEEK_CUR)
664 # Mouse over fft display - show frequency in tooltip
665 def Mouse(self,event):
667 fRel = ( event.GetX() - 330. ) / 14.266666 - 7.5
669 fRel = ( event.GetX() - 330. ) / 14.266666
670 self.fft.win.SetToolTip(wx.ToolTip(eng_notation.num_to_str(self.frequency + (fRel*1e3))))
672 # Mouse clicked on fft display - change frequency
673 def Click(self,event):
674 fRel = ( event.GetX() - 330. ) / 14.266666
675 if self.AM_mode == False:
676 self.frequency = self.frequency + (fRel*1e3)
678 self.frequency = self.frequency + (fRel*1e3) - 7.5e3
679 self.spin_ctrl_1.SetValue(int(self.frequency))
680 self.slider_3.SetValue(int((self.frequency-self.f_slider_offset)/self.f_slider_scale))
681 if self.AM_mode == False:
682 self.xlate.set_center_freq ( self.usrp_center - ( self.frequency - self.tune_offset ))
684 self.xlate.set_center_freq( self.usrp_center - (self.frequency - self.tune_offset - 7.5e3))
686 # Set power of AM sync carrier
687 def am_carrier(self,event):
688 scale = math.pow(10,(self.slider_6.GetValue())/50.)
689 self.pll_carrier_scale.set_k(complex(scale,0))
691 # Reset AT data and start calibrate over
692 def AT_reset(self, event):
696 # Save AT setting for a particular frequency
697 def AT_calibrate(self, event):
698 self.xdata.append(float(self.frequency))
699 self.ydata.append(self.slider_7.GetValue())
700 if len(self.xdata) > 1:
703 for i in range(0,len(self.xdata)-1):
704 self.m.append( (self.ydata[i+1] - self.ydata[i]) / (self.xdata[i+1] - self.xdata[i]) )
705 self.b.append( self.ydata[i] - self.m[i] * self.xdata[i] )
707 # Lookup calibrated points and calculate interpolated antenna tune voltage.
708 # This is to automatically tune a narrowband loop antenna when the freq
709 # is changed, to keep signals peaked.
710 def auto_antenna_tune(self):
711 for i in range(0,len(self.xdata)-1):
712 if (self.frequency > self.xdata[i]) & (self.frequency < self.xdata[i+1]):
713 self.slider_7.SetValue(self.m[i]*self.frequency + self.b[i])
716 # Slider to set loop antenna capacitance
717 def antenna_tune(self, evt):
718 if self.PLAY_FROM_USRP:
719 self.src.write_aux_dac(0,3,self.slider_7.GetValue())
721 # Timer events - check for web commands
723 cmds = os.listdir("/var/www/cgi-bin/commands/")
725 if cmds[0]=='chfreq':
726 fd=open("/var/www/cgi-bin/commands/chfreq","r")
730 os.unlink("/var/www/cgi-bin/commands/chfreq")
731 if ( int(new) >= self.f_lo ) & ( int(new) <= self.f_hi ):
732 self.frequency = int(new)
733 self.slider_3.SetValue(( self.frequency - self.f_slider_offset) / self.f_slider_scale )
734 self.spin_ctrl_1.SetValue(self.frequency)
735 if self.button_12.GetValue():
736 self.auto_antenna_tune()
738 self.xlate.set_center_freq ( self.usrp_center - ( self.frequency - self.tune_offset - 7.5e3 ))
740 self.xlate.set_center_freq ( self.usrp_center - ( self.frequency - self.tune_offset ))
742 if cmds[0]=='chvolume':
743 fd=open("/var/www/cgi-bin/commands/chvolume","r")
747 os.unlink("/var/www/cgi-bin/commands/chvolume")
748 if ( int(new) >= 0 ) & ( int(new) <= 500 ):
749 self.volume = int(new)
750 self.slider_4.SetValue(self.volume)
751 self.scale.set_k(math.pow(10.,((self.slider_4.GetValue()-500.)/100.)))
753 else: # no new web commands, update state
754 fh = open("/var/www/cgi-bin/state/freq","w")
755 fh.write(str(int(self.frequency))+'\n')
757 fh = open("/var/www/cgi-bin/state/volume","w")
758 fh.write(str(self.slider_4.GetValue())+'\n')
762 # end of class MyFrame
764 # wx.Timer to check for web updates
765 class UpdateTimer(wx.Timer):
766 def __init__(self, target, dur=1000):
767 wx.Timer.__init__(self)
772 """Called every timer interval"""
774 self.target.OnUpdate()
779 frame = MyFrame(None, -1, "HF Explorer 2")
781 self.SetTopWindow(frame)