diff options
author | Josh Blum | 2011-10-19 16:37:33 -0700 |
---|---|---|
committer | Josh Blum | 2011-10-19 16:37:33 -0700 |
commit | be300d920fe1b7a3eb738565cddd10624ec62a75 (patch) | |
tree | 45bb49c4a4acd7c93bb218c84b94de4c90f198c5 /gr-radio-astronomy/src | |
parent | a57f7484a1b1ba18b66334675ce6458b00d40626 (diff) | |
parent | e30b824e9165bff69f09121631c3d5a706cbbd39 (diff) | |
download | gnuradio-be300d920fe1b7a3eb738565cddd10624ec62a75.tar.gz gnuradio-be300d920fe1b7a3eb738565cddd10624ec62a75.tar.bz2 gnuradio-be300d920fe1b7a3eb738565cddd10624ec62a75.zip |
Merge branch 'next' of http://gnuradio.org/git/gnuradio into next
Diffstat (limited to 'gr-radio-astronomy/src')
18 files changed, 0 insertions, 4834 deletions
diff --git a/gr-radio-astronomy/src/.gitignore b/gr-radio-astronomy/src/.gitignore deleted file mode 100644 index bb3f27777..000000000 --- a/gr-radio-astronomy/src/.gitignore +++ /dev/null @@ -1,10 +0,0 @@ -/Makefile -/Makefile.in -/.la -/.lo -/.deps -/.libs -/*.la -/*.lo -/howto.cc -/howto.py diff --git a/gr-radio-astronomy/src/Makefile.am b/gr-radio-astronomy/src/Makefile.am deleted file mode 100644 index be38b7c1a..000000000 --- a/gr-radio-astronomy/src/Makefile.am +++ /dev/null @@ -1,25 +0,0 @@ -# -# Copyright 2004 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. -# - -SUBDIRS = lib -if PYTHON -SUBDIRS += python -endif diff --git a/gr-radio-astronomy/src/lib/.gitignore b/gr-radio-astronomy/src/lib/.gitignore deleted file mode 100644 index 711bfef2c..000000000 --- a/gr-radio-astronomy/src/lib/.gitignore +++ /dev/null @@ -1,16 +0,0 @@ -/Makefile -/Makefile.in -/.la -/.lo -/.deps -/.libs -/*.la -/*.lo -/howto.cc -/howto.py -/ra.cc -/ra.py -/*.pyc -/gnuradio -/guile -/python diff --git a/gr-radio-astronomy/src/lib/Makefile.am b/gr-radio-astronomy/src/lib/Makefile.am deleted file mode 100644 index 1b6b854ea..000000000 --- a/gr-radio-astronomy/src/lib/Makefile.am +++ /dev/null @@ -1,35 +0,0 @@ -# -# Copyright 2004,2005,2006,2008,2009,2010 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 -include $(top_srcdir)/Makefile.swig - -AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) $(PYTHON_CPPFLAGS) $(WITH_INCLUDES) - -TOP_SWIG_IFILES = \ - ra.i - -# Install so that they end up available as: -# import gnuradio.ra -# This ends up at: -# ${prefix}/lib/python${python_version}/site-packages/gnuradio -ra_pythondir_category = \ - gnuradio diff --git a/gr-radio-astronomy/src/lib/Makefile.swig.gen b/gr-radio-astronomy/src/lib/Makefile.swig.gen deleted file mode 100644 index faad880cf..000000000 --- a/gr-radio-astronomy/src/lib/Makefile.swig.gen +++ /dev/null @@ -1,145 +0,0 @@ -# -*- Makefile -*- -# -# Copyright 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. -# - -# Makefile.swig.gen for ra.i - -## Default install locations for these files: -## -## Default location for the Python directory is: -## ${prefix}/lib/python${python_version}/site-packages/[category]/ra -## Default location for the Python exec directory is: -## ${exec_prefix}/lib/python${python_version}/site-packages/[category]/ra -## -## The following can be overloaded to change the install location, but -## this has to be done in the including Makefile.am -before- -## Makefile.swig is included. - -ra_pythondir_category ?= gnuradio/ra -ra_pylibdir_category ?= $(ra_pythondir_category) -ra_pythondir = $(pythondir)/$(ra_pythondir_category) -ra_pylibdir = $(pyexecdir)/$(ra_pylibdir_category) - -# The .so libraries for the guile modules get installed whereever guile -# is installed, usually /usr/lib/guile/gnuradio/ -# FIXME: determince whether these should be installed with gnuradio. -ra_scmlibdir = $(libdir) - -# The scm files for the guile modules get installed where ever guile -# is installed, usually /usr/share/guile/site/ra -# FIXME: determince whether these should be installed with gnuradio. -ra_scmdir = $(guiledir) - -## SWIG headers are always installed into the same directory. - -ra_swigincludedir = $(swigincludedir) - -## This is a template file for a "generated" Makefile addition (in -## this case, "Makefile.swig.gen"). By including the top-level -## Makefile.swig, this file will be used to generate the SWIG -## dependencies. Assign the variable TOP_SWIG_FILES to be the list of -## SWIG .i files to generated wrappings for; there can be more than 1 -## so long as the names are unique (no sorting is done on the -## TOP_SWIG_FILES list). This file explicitly assumes that a SWIG .i -## file will generate .cc, .py, and possibly .h files -- meaning that -## all of these files will have the same base name (that provided for -## the SWIG .i file). -## -## This code is setup to ensure parallel MAKE ("-j" or "-jN") does the -## right thing. For more info, see < -## http://sources.redhat.com/automake/automake.html#Multiple-Outputs > - -## Other cleaned files: dependency files generated by SWIG or this Makefile - -MOSTLYCLEANFILES += $(DEPDIR)/*.S* - -## Various SWIG variables. These can be overloaded in the including -## Makefile.am by setting the variable value there, then including -## Makefile.swig . - -ra_swiginclude_HEADERS = \ - ra.i \ - $(ra_swiginclude_headers) - -if PYTHON -ra_pylib_LTLIBRARIES = \ - _ra.la - -_ra_la_SOURCES = \ - python/ra.cc \ - $(ra_la_swig_sources) - -ra_python_PYTHON = \ - ra.py \ - $(ra_python) - -_ra_la_LIBADD = \ - $(STD_SWIG_LA_LIB_ADD) \ - $(ra_la_swig_libadd) - -_ra_la_LDFLAGS = \ - $(STD_SWIG_LA_LD_FLAGS) \ - $(ra_la_swig_ldflags) - -_ra_la_CXXFLAGS = \ - $(STD_SWIG_CXX_FLAGS) \ - -I$(top_builddir) \ - $(ra_la_swig_cxxflags) - -python/ra.cc: ra.py -ra.py: ra.i - -# Include the python dependencies for this file --include python/ra.d - -endif # end of if python - -if GUILE - -ra_scmlib_LTLIBRARIES = \ - libguile-gnuradio-ra.la -libguile_gnuradio_ra_la_SOURCES = \ - guile/ra.cc \ - $(ra_la_swig_sources) -nobase_ra_scm_DATA = \ - gnuradio/ra.scm \ - gnuradio/ra-primitive.scm -libguile_gnuradio_ra_la_LIBADD = \ - $(STD_SWIG_LA_LIB_ADD) \ - $(ra_la_swig_libadd) -libguile_gnuradio_ra_la_LDFLAGS = \ - $(STD_SWIG_LA_LD_FLAGS) \ - $(ra_la_swig_ldflags) -libguile_gnuradio_ra_la_CXXFLAGS = \ - $(STD_SWIG_CXX_FLAGS) \ - -I$(top_builddir) \ - $(ra_la_swig_cxxflags) - -guile/ra.cc: gnuradio/ra.scm -gnuradio/ra.scm: ra.i -gnuradio/ra-primitive.scm: gnuradio/ra.scm - -# Include the guile dependencies for this file --include guile/ra.d - -endif # end of GUILE - - diff --git a/gr-radio-astronomy/src/lib/ra.i b/gr-radio-astronomy/src/lib/ra.i deleted file mode 100644 index 3e8f409e3..000000000 --- a/gr-radio-astronomy/src/lib/ra.i +++ /dev/null @@ -1,67 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 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 "gnuradio.i" // the common stuff - -// We'll need something like this if we add C++ blocks... -// See the gr-howto-write-a-block tarball for examples - -// ---------------------------------------------------------------- - -/* - * First arg is the package prefix. - * Second arg is the name of the class minus the prefix. - * - * This does some behind-the-scenes magic so we can - * access ra_square_ff from python as ra.square_ff - */ -// GR_SWIG_BLOCK_MAGIC(ra,square_ff); - -// ra_square_ff_sptr ra_make_square_ff (); -// -// class ra_square_ff : public gr_block -// { -// private: -// ra_square_ff (); -// }; -// -// // ---------------------------------------------------------------- -// -// GR_SWIG_BLOCK_MAGIC(ra,square2_ff); -// -// ra_square2_ff_sptr ra_make_square2_ff (); -// -// class ra_square2_ff : public gr_sync_block -// { -// private: -// ra_square2_ff (); -// }; - -#if SWIGGUILE -%scheme %{ -(load-extension-global "libguile-gnuradio-ra" "scm_init_gnuradio_ra_module") -%} - -%goops %{ -(use-modules (gnuradio gnuradio_core_runtime)) -%} -#endif diff --git a/gr-radio-astronomy/src/python/.gitignore b/gr-radio-astronomy/src/python/.gitignore deleted file mode 100644 index bf03975bb..000000000 --- a/gr-radio-astronomy/src/python/.gitignore +++ /dev/null @@ -1,9 +0,0 @@ -/Makefile -/Makefile.in -/.deps -/.libs -/*.la -/*.lo -/*.pyc -/*.pyo -/run_tests diff --git a/gr-radio-astronomy/src/python/Makefile.am b/gr-radio-astronomy/src/python/Makefile.am deleted file mode 100644 index 2518e0647..000000000 --- a/gr-radio-astronomy/src/python/Makefile.am +++ /dev/null @@ -1,56 +0,0 @@ -# -# Copyright 2004,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 - -# Install this stuff so that it ends up as the gnuradio.ra module -# This usually ends up at: -# ${prefix}/lib/python${python_version}/site-packages/gnuradio - -ourpythondir = $(grpythondir) -ourlibdir = $(grpyexecdir) - -# wxgui stuff here -wxguipythondir = $(grpythondir)/wxgui -wxguilibdir = $(grpyexecdir)/wxgui - -EXTRA_DIST += \ - run_tests.in - -TESTS = run_tests - - -noinst_PYTHON = \ - qa_ra.py - -ourpython_PYTHON = \ - local_calibrator.py - -wxguipython_PYTHON = \ - ra_stripchartsink.py \ - ra_fftsink.py \ - ra_waterfallsink.py - - -# and here for applications you want installed in prefix/bin -dist_bin_SCRIPTS = \ - usrp_ra_receiver.py \ - usrp_psr_receiver.py diff --git a/gr-radio-astronomy/src/python/local_calibrator.py b/gr-radio-astronomy/src/python/local_calibrator.py deleted file mode 100755 index d76060c0f..000000000 --- a/gr-radio-astronomy/src/python/local_calibrator.py +++ /dev/null @@ -1,195 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2003,2004,2005 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. -# - -import Numeric -import math -import ephem -import time - -# -#!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -# NO LONGER USED -#!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -# -# - - -# -# Simple class for allowing local definition of a calibration function -# for raw samples coming from the RA detector chain. Each observatory -# is different, and rather than hacking up the main code in usrp_ra_receiver -# we define the appropriate function here. -# -# For example, one could calibrate the output in Janskys, rather than -# dB. -# -# - -def calib_default_total_power(data): - r = 10.0*math.log10(data) - return(r) - -def calib_numogate_ridge_observatory_total_power(data): - - me = ephem.Observer() - - # - # PyEphem wants lat/long as strings, rather than floats--took me quite - # a long time to figure that out. If they don't arrive as strings, - # the calculations for sidereal time are complete garbage - # - me.long = globals()["calib_long"] - me.lat = globals()["calib_lat"] - - me.date = ephem.now() - sidtime = me.sidereal_time() - - foo = time.localtime() - if not "calib_prefix" in globals(): - pfx = "./" - else: - pfx = globals()["calib_prefix"] - filenamestr = "%s/%04d%02d%02d%02d" % (pfx, foo.tm_year, - foo.tm_mon, foo.tm_mday, foo.tm_hour) - - numogate_file = open (filenamestr+".tpdat","a") - - r = (data / 409.6) - flt = "%6.3f" % r - #r = calib_default_total_power(data) - inter = globals()["calib_decln"] - integ = globals()["calib_integ_setting"] - fc = globals()["calib_freq_setting"] - fc = fc / 1000000 - bw = globals()["calib_bw_setting"] - bw = bw / 1000000 - ga = globals()["calib_gain_setting"] - - now = time.time() - - if not "calib_then_tpdat" in globals(): - globals()["calib_then_tpdat"] = now - - if (now - globals()["calib_then_tpdat"]) >= 20: - globals()["calib_then_tpdat"] = now - - numogate_file.write(str(ephem.hours(sidtime))+" "+flt+" Dn="+str(inter)+",") - numogate_file.write("Ti="+str(integ)+",Fc="+str(fc)+",Bw="+str(bw)) - numogate_file.write(",Ga="+str(ga)+"\n") - else: - numogate_file.write(str(ephem.hours(sidtime))+" "+flt+"\n") - - numogate_file.close() - return(r) - -def calib_numogate_ridge_observatory_fft(data,l): - - me = ephem.Observer() - - # - # PyEphem wants lat/long as strings, rather than floats--took me quite - # a long time to figure that out. If they don't arrive as strings, - # the calculations for sidereal time are complete garbage - # - me.long = globals()["calib_long"] - me.lat = globals()["calib_lat"] - - me.date = ephem.now() - sidtime = me.sidereal_time() - - foo = time.localtime() - - if not "calib_prefix" in globals(): - pfx = "./" - else: - pfx = globals()["calib_prefix"] - filenamestr = "%s/%04d%02d%02d%02d" % (pfx, foo.tm_year, - foo.tm_mon, foo.tm_mday, foo.tm_hour) - - now = time.time() - - if not "calib_then" in globals(): - globals()["calib_then"] = now - - delta = (l/1024)*5 - - if (now - globals()["calib_then"]) >= delta: - - globals()["calib_then"] = now - numogate_file = open (filenamestr+".sdat","a") - - r = data - inter = globals()["calib_decln"] - fc = globals()["calib_freq_setting"] - fc = fc / 1000000 - bw = globals()["calib_bw_setting"] - bw = bw / 1000000 - av = globals()["calib_avg_alpha"] - numogate_file.write("data:"+str(ephem.hours(sidtime))+" Dn="+str(inter)+",Fc="+str(fc)+",Bw="+str(bw)+",Av="+str(av)) - numogate_file.write(" "+str(r)+"\n") - numogate_file.close() - return(r) - - return(data) - -def calib_default_fft(db,l): - return(db) - -# -# We capture various parameters from the receive chain here, because -# they can affect the calibration equations. -# -# -def calib_set_gain(gain): - globals()["calib_gain_setting"] = gain - globals()["calib_then_tpdat"] = time.time() - 50 - -def calib_set_integ(integ): - globals()["calib_integ_setting"] = integ - globals()["calib_then_tpdat"] = time.time() - 50 - -def calib_set_bw(bw): - globals()["calib_bw_setting"] = bw - globals()["calib_then_tpdat"] = time.time() - 50 - -def calib_set_freq(freq): - globals()["calib_freq_setting"] = freq - globals()["calib_then_tpdat"] = time.time() - 50 - -def calib_set_avg_alpha(alpha): - globals()["calib_avg_alpha"] = alpha - -def calib_set_interesting(inter): - globals()["calib_is_interesting"] = inter - -def calib_set_decln(dec): - globals()["calib_decln"] = dec - globals()["calib_then_tpdat"] = time.time() - 50 - -def calib_set_prefix(pfx): - globals()["calib_prefix"] = pfx - -def calib_set_long(long): - globals()["calib_long"] = long - -def calib_set_lat(lat): - globals()["calib_lat"] = lat diff --git a/gr-radio-astronomy/src/python/qa_ra.py b/gr-radio-astronomy/src/python/qa_ra.py deleted file mode 100755 index 2cb0f42be..000000000 --- a/gr-radio-astronomy/src/python/qa_ra.py +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2004,2006,2010 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. -# - -from gnuradio import gr, gr_unittest -import ra - -class test_radio_astronomy (gr_unittest.TestCase): - - def setUp (self): - self.tb = gr.top_block () - - def tearDown (self): - self.tb = None - - def test_000_(self): # ensure that we can load the module - pass - -if __name__ == '__main__': - gr_unittest.run(test_radio_astronomy, "test_radio_astronomy.xml") diff --git a/gr-radio-astronomy/src/python/ra_fftsink.py b/gr-radio-astronomy/src/python/ra_fftsink.py deleted file mode 100755 index 4733027c1..000000000 --- a/gr-radio-astronomy/src/python/ra_fftsink.py +++ /dev/null @@ -1,501 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2003,2004,2005,2007 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. -# - -from gnuradio import gr, gru, window -from gnuradio.wxgui import stdgui2 -import wx -import gnuradio.wxgui.plot as plot -import numpy -import threading -import math -import random - -default_ra_fftsink_size = (640,140) - - - -class ra_fft_sink_base(object): - def __init__(self, input_is_real=False, baseband_freq=0, y_per_div=10, sc_y_per_div=0.5, ref_level=50, sc_ref_level=20, - sample_rate=1, fft_size=512, fft_rate=15, - average=False, avg_alpha=None, title='', peak_hold=False, ofunc=None, xydfunc=None): - - # initialize common attributes - self.baseband_freq = baseband_freq - self.y_divs = 8 - self.y_per_div=y_per_div - self.sc_y_per_div=sc_y_per_div - self.ref_level = ref_level - self.autoscale = False - self.sc_ref_level = sc_ref_level - self.sample_rate = sample_rate - self.fft_size = fft_size - self.fft_rate = fft_rate - self.binwidth = float(sample_rate/fft_size) - self.average = average - self.ofunc = ofunc - self.xydfunc = xydfunc - self.ofunc = ofunc - if avg_alpha is None: - self.avg_alpha = 2.0 / fft_rate - else: - self.avg_alpha = avg_alpha - self.title = title - self.peak_hold = peak_hold - self.input_is_real = input_is_real - self.msgq = gr.msg_queue(2) # queue that holds a maximum of 2 messages - - def set_y_per_div(self, y_per_div): - self.y_per_div = y_per_div - - - def set_ref_level(self, ref_level): - self.ref_level = ref_level - - def set_average(self, average): - self.average = average - if average: - self.avg.set_taps(self.avg_alpha) - self.set_peak_hold(False) - else: - self.avg.set_taps(1.0) - - def set_peak_hold(self, enable): - self.peak_hold = enable - if enable: - self.set_average(False) - self.win.set_peak_hold(enable) - - def set_autoscale(self, auto): - self.autoscale = auto - - def set_avg_alpha(self, avg_alpha): - self.avg_alpha = avg_alpha - - def set_baseband_freq(self, baseband_freq): - self.baseband_freq = baseband_freq - - -class ra_fft_sink_f(gr.hier_block2, ra_fft_sink_base): - def __init__(self, parent, baseband_freq=0, - y_per_div=10, sc_y_per_div=0.5, sc_ref_level=40, ref_level=50, sample_rate=1, fft_size=512, - fft_rate=15, average=False, avg_alpha=None, title='', - size=default_ra_fftsink_size, peak_hold=False, ofunc=None, - xydfunc=None): - gr.hier_block2.__init__(self, "ra_fft_sink_f", - gr.io_signature(1, 1, gr.sizeof_float), - gr.io_signature(0, 0, 0)) - - ra_fft_sink_base.__init__(self, input_is_real=True, baseband_freq=baseband_freq, - y_per_div=y_per_div, sc_y_per_div=sc_y_per_div, - sc_ref_level=sc_ref_level, ref_level=ref_level, - sample_rate=sample_rate, fft_size=fft_size, - fft_rate=fft_rate, - average=average, avg_alpha=avg_alpha, title=title, - peak_hold=peak_hold, ofunc=ofunc, - xydfunc=xydfunc) - - self.binwidth = float(sample_rate/2.0)/float(fft_size) - s2p = gr.serial_to_parallel(gr.sizeof_float, fft_size) - one_in_n = gr.keep_one_in_n(gr.sizeof_float * fft_size, - max(1, int(sample_rate/fft_size/fft_rate))) - mywindow = window.blackmanharris(fft_size) - fft = gr.fft_vfc(fft_size, True, mywindow) - c2mag = gr.complex_to_mag(fft_size) - self.avg = gr.single_pole_iir_filter_ff(1.0, fft_size) - log = gr.nlog10_ff(20, fft_size, -20*math.log10(fft_size)) - sink = gr.message_sink(gr.sizeof_float * fft_size, self.msgq, True) - - self.connect (self, s2p, one_in_n, fft, c2mag, self.avg, log, sink) - - self.win = fft_window(self, parent, size=size) - self.set_average(self.average) - -class ra_fft_sink_c(gr.hier_block2, ra_fft_sink_base): - def __init__(self, parent, baseband_freq=0, - y_per_div=10, sc_y_per_div=0.5, sc_ref_level=40, - ref_level=50, sample_rate=1, fft_size=512, - fft_rate=15, average=False, avg_alpha=None, title='', - size=default_ra_fftsink_size, peak_hold=False, ofunc=None, xydfunc=None): - - gr.hier_block2.__init__(self, "ra_fft_sink_c", - gr.io_signature(1, 1, gr.sizeof_gr_complex), - gr.io_signature(0, 0, 0)) - - - ra_fft_sink_base.__init__(self, input_is_real=False, baseband_freq=baseband_freq, - y_per_div=y_per_div, sc_y_per_div=sc_y_per_div, - sc_ref_level=sc_ref_level, ref_level=ref_level, - sample_rate=sample_rate, fft_size=fft_size, - fft_rate=fft_rate, - average=average, avg_alpha=avg_alpha, - title=title, - peak_hold=peak_hold, ofunc=ofunc, - xydfunc=xydfunc) - - s2p = gr.serial_to_parallel(gr.sizeof_gr_complex, fft_size) - one_in_n = gr.keep_one_in_n(gr.sizeof_gr_complex * fft_size, - max(1, int(sample_rate/fft_size/fft_rate))) - mywindow = window.blackmanharris(fft_size) - fft = gr.fft_vcc(fft_size, True, mywindow) - c2mag = gr.complex_to_mag(fft_size) - self.avg = gr.single_pole_iir_filter_ff(1.0, fft_size) - log = gr.nlog10_ff(20, fft_size, -20*math.log10(fft_size)) - sink = gr.message_sink(gr.sizeof_float * fft_size, self.msgq, True) - - self.connect(self, s2p, one_in_n, fft, c2mag, self.avg, log, sink) - - self.win = fft_window(self, parent, size=size) - self.set_average(self.average) - - -# ------------------------------------------------------------------------ - -myDATA_EVENT = wx.NewEventType() -EVT_DATA_EVENT = wx.PyEventBinder (myDATA_EVENT, 0) - - -class DataEvent(wx.PyEvent): - def __init__(self, data): - wx.PyEvent.__init__(self) - self.SetEventType (myDATA_EVENT) - self.data = data - - def Clone (self): - self.__class__ (self.GetId()) - - -class input_watcher (threading.Thread): - def __init__ (self, msgq, fft_size, event_receiver, **kwds): - threading.Thread.__init__ (self, **kwds) - self.setDaemon (1) - self.msgq = msgq - self.fft_size = fft_size - self.event_receiver = event_receiver - self.keep_running = True - self.start () - - def run (self): - while (self.keep_running): - msg = self.msgq.delete_head() # blocking read of message queue - itemsize = int(msg.arg1()) - nitems = int(msg.arg2()) - - s = msg.to_string() # get the body of the msg as a string - - # There may be more than one FFT frame in the message. - # If so, we take only the last one - if nitems > 1: - start = itemsize * (nitems - 1) - s = s[start:start+itemsize] - - complex_data = numpy.fromstring (s, numpy.float32) - de = DataEvent (complex_data) - wx.PostEvent (self.event_receiver, de) - del de - - -class fft_window (plot.PlotCanvas): - def __init__ (self, ra_fftsink, parent, id = -1, - pos = wx.DefaultPosition, size = wx.DefaultSize, - style = wx.DEFAULT_FRAME_STYLE, name = ""): - plot.PlotCanvas.__init__ (self, parent, id, pos, size, style, name) - - self.y_range = None - self.ra_fftsink = ra_fftsink - self.peak_hold = False - self.peak_vals = None - - self.SetEnableGrid (True) - # self.SetEnableZoom (True) - # self.SetBackgroundColour ('black') - - self.build_popup_menu() - - EVT_DATA_EVENT (self, self.set_data) - wx.EVT_CLOSE (self, self.on_close_window) - self.Bind(wx.EVT_RIGHT_UP, self.on_right_click) - self.Bind(wx.EVT_LEFT_UP, self.on_left_click) - self.Bind(wx.EVT_MOTION, self.on_motion) - - self.input_watcher = input_watcher(ra_fftsink.msgq, ra_fftsink.fft_size, self) - - - def on_close_window (self, event): - print "fft_window:on_close_window" - self.keep_running = False - - - def set_data (self, evt): - calc_min = 99e10 - calc_max = -99e10 - dB = evt.data - L = len (dB) - - calc_min = min(dB) - calc_max = max(dB) - - if (self.ra_fftsink.ofunc != None): - self.ra_fftsink.ofunc(evt.data,L) - - if self.peak_hold: - if self.peak_vals is None: - self.peak_vals = dB - else: - self.peak_vals = numpy.maximum(dB, self.peak_vals) - dB = self.peak_vals - - x = max(abs(self.ra_fftsink.sample_rate), abs(self.ra_fftsink.baseband_freq)) - if x >= 1e9: - sf = 1e-9 - units = "GHz" - elif x >= 1e6: - sf = 1e-6 - units = "MHz" - elif x >= 1e3: - sf = 1e-3 - units = "kHz" - else: - sf = 1.0 - units = "Hz" - - if self.ra_fftsink.input_is_real: # only plot 1/2 the points - x_vals = ((numpy.arange (L/2) - * (self.ra_fftsink.sample_rate * sf / L)) - + self.ra_fftsink.baseband_freq * sf) - points = numpy.zeros((len(x_vals), 2), numpy.float64) - points[:,0] = x_vals - points[:,1] = dB[0:L/2] - else: - # the "negative freqs" are in the second half of the array - x_vals = ((numpy.arange(-L/2, L/2) - * (self.ra_fftsink.sample_rate * sf / L)) - + self.ra_fftsink.baseband_freq * sf) - points = numpy.zeros((len(x_vals), 2), numpy.float64) - points[:,0] = x_vals - points[:,1] = numpy.concatenate ((dB[L/2:], dB[0:L/2])) - - lines = plot.PolyLine (points, colour='BLUE') - graphics = plot.PlotGraphics ([lines], - title=self.ra_fftsink.title, - xLabel = units, yLabel = "dB") - - self.Draw (graphics, xAxis=None, yAxis=self.y_range) - d = calc_max - calc_min - d = d * 0.1 - if self.ra_fftsink.autoscale == True: - self.y_range = self._axisInterval ('min', calc_min-d, calc_max+d) - else: - self.update_y_range () - - def set_peak_hold(self, enable): - self.peak_hold = enable - self.peak_vals = None - - def update_y_range (self): - ymax = self.ra_fftsink.ref_level - ymin = self.ra_fftsink.ref_level - self.ra_fftsink.y_per_div * self.ra_fftsink.y_divs - self.y_range = self._axisInterval ('min', ymin, ymax) - - def on_average(self, evt): - # print "on_average" - self.ra_fftsink.set_average(evt.IsChecked()) - - def on_peak_hold(self, evt): - # print "on_peak_hold" - self.ra_fftsink.set_peak_hold(evt.IsChecked()) - - def on_autoscale(self, evt): - self.ra_fftsink.set_autoscale(evt.IsChecked()) - - def on_incr_ref_level(self, evt): - # print "on_incr_ref_level" - self.ra_fftsink.set_ref_level(self.ra_fftsink.ref_level - + self.ra_fftsink.y_per_div) - - def on_decr_ref_level(self, evt): - # print "on_decr_ref_level" - self.ra_fftsink.set_ref_level(self.ra_fftsink.ref_level - - self.ra_fftsink.y_per_div) - - def on_incr_y_per_div(self, evt): - # print "on_incr_y_per_div" - self.ra_fftsink.set_y_per_div(next_up(self.ra_fftsink.y_per_div, (0.5,1,2,5,10))) - - def on_decr_y_per_div(self, evt): - # print "on_decr_y_per_div" - self.ra_fftsink.set_y_per_div(next_down(self.ra_fftsink.y_per_div, (0.5,1,2,5,10))) - - def on_y_per_div(self, evt): - # print "on_y_per_div" - Id = evt.GetId() - if Id == self.id_y_per_div_1: - self.ra_fftsink.set_y_per_div(0.5) - elif Id == self.id_y_per_div_2: - self.ra_fftsink.set_y_per_div(1.0) - elif Id == self.id_y_per_div_5: - self.ra_fftsink.set_y_per_div(2.0) - elif Id == self.id_y_per_div_10: - self.ra_fftsink.set_y_per_div(5.0) - elif Id == self.id_y_per_div_20: - self.ra_fftsink.set_y_per_div(10) - - - def on_right_click(self, event): - menu = self.popup_menu - for id, pred in self.checkmarks.items(): - item = menu.FindItemById(id) - item.Check(pred()) - self.PopupMenu(menu, event.GetPosition()) - - def on_motion(self, event): - if not self.ra_fftsink.xydfunc == None: - xy = self.GetXY(event) - self.ra_fftsink.xydfunc (0,xy) - - def on_left_click(self,event): - if not self.ra_fftsink.xydfunc == None: - xy = self.GetXY(event) - self.ra_fftsink.xydfunc (1,xy) - - def build_popup_menu(self): - self.id_incr_ref_level = wx.NewId() - self.id_decr_ref_level = wx.NewId() - self.id_autoscale = wx.NewId() - self.id_incr_y_per_div = wx.NewId() - self.id_decr_y_per_div = wx.NewId() - self.id_y_per_div_1 = wx.NewId() - self.id_y_per_div_2 = wx.NewId() - self.id_y_per_div_5 = wx.NewId() - self.id_y_per_div_10 = wx.NewId() - self.id_y_per_div_20 = wx.NewId() - self.id_average = wx.NewId() - self.id_peak_hold = wx.NewId() - - self.Bind(wx.EVT_MENU, self.on_average, id=self.id_average) - self.Bind(wx.EVT_MENU, self.on_peak_hold, id=self.id_peak_hold) - self.Bind(wx.EVT_MENU, self.on_autoscale, id=self.id_autoscale) - self.Bind(wx.EVT_MENU, self.on_incr_ref_level, id=self.id_incr_ref_level) - self.Bind(wx.EVT_MENU, self.on_decr_ref_level, id=self.id_decr_ref_level) - self.Bind(wx.EVT_MENU, self.on_incr_y_per_div, id=self.id_incr_y_per_div) - self.Bind(wx.EVT_MENU, self.on_decr_y_per_div, id=self.id_decr_y_per_div) - self.Bind(wx.EVT_MENU, self.on_y_per_div, id=self.id_y_per_div_1) - self.Bind(wx.EVT_MENU, self.on_y_per_div, id=self.id_y_per_div_2) - self.Bind(wx.EVT_MENU, self.on_y_per_div, id=self.id_y_per_div_5) - self.Bind(wx.EVT_MENU, self.on_y_per_div, id=self.id_y_per_div_10) - self.Bind(wx.EVT_MENU, self.on_y_per_div, id=self.id_y_per_div_20) - - - # make a menu - menu = wx.Menu() - self.popup_menu = menu - menu.AppendCheckItem(self.id_average, "Average") - menu.AppendCheckItem(self.id_peak_hold, "Peak Hold") - menu.Append(self.id_incr_ref_level, "Incr Ref Level") - menu.Append(self.id_decr_ref_level, "Decr Ref Level") - # menu.Append(self.id_incr_y_per_div, "Incr dB/div") - # menu.Append(self.id_decr_y_per_div, "Decr dB/div") - menu.AppendSeparator() - # we'd use RadioItems for these, but they're not supported on Mac - menu.AppendCheckItem(self.id_autoscale, "Autoscale") - menu.AppendCheckItem(self.id_y_per_div_1, "0.5 dB/div") - menu.AppendCheckItem(self.id_y_per_div_2, "1.0 dB/div") - menu.AppendCheckItem(self.id_y_per_div_5, "2.0 dB/div") - menu.AppendCheckItem(self.id_y_per_div_10, "5.0 dB/div") - menu.AppendCheckItem(self.id_y_per_div_20, "10.0 dB/div") - - self.checkmarks = { - self.id_average : lambda : self.ra_fftsink.average, - self.id_peak_hold : lambda : self.ra_fftsink.peak_hold, - self.id_autoscale : lambda : self.ra_fftsink.autoscale, - self.id_y_per_div_1 : lambda : self.ra_fftsink.y_per_div == 0.5, - self.id_y_per_div_2 : lambda : self.ra_fftsink.y_per_div == 1.0, - self.id_y_per_div_5 : lambda : self.ra_fftsink.y_per_div == 2.0, - self.id_y_per_div_10 : lambda : self.ra_fftsink.y_per_div == 5.0, - self.id_y_per_div_20 : lambda : self.ra_fftsink.y_per_div == 10.0, - } - - -def next_up(v, seq): - """ - Return the first item in seq that is > v. - """ - for s in seq: - if s > v: - return s - return v - -def next_down(v, seq): - """ - Return the last item in seq that is < v. - """ - rseq = list(seq[:]) - rseq.reverse() - - for s in rseq: - if s < v: - return s - return v - - -# ---------------------------------------------------------------- -# Standalone test app -# ---------------------------------------------------------------- - -class test_app_flow_graph (stdgui2.std_top_block): - def __init__(self, frame, panel, vbox, argv): - stdgui2.std_top_block.__init__ (self, frame, panel, vbox, argv) - - fft_size = 256 - - # build our flow graph - input_rate = 20.000e3 - - # Generate a complex sinusoid - src1 = gr.sig_source_c (input_rate, gr.GR_SIN_WAVE, 5.75e3, 1000) - #src1 = gr.sig_source_c (input_rate, gr.GR_CONST_WAVE, 5.75e3, 1000) - - # We add these throttle blocks so that this demo doesn't - # suck down all the CPU available. Normally you wouldn't use these. - thr1 = gr.throttle(gr.sizeof_gr_complex, input_rate) - - sink1 = ra_fft_sink_c (panel, title="Complex Data", fft_size=fft_size, - sample_rate=input_rate, baseband_freq=100e3, - ref_level=60, y_per_div=10) - vbox.Add (sink1.win, 1, wx.EXPAND) - self.connect (src1, thr1, sink1) - - src2 = gr.sig_source_f (input_rate, gr.GR_SIN_WAVE, 5.75e3, 1000) - #src2 = gr.sig_source_f (input_rate, gr.GR_CONST_WAVE, 5.75e3, 1000) - thr2 = gr.throttle(gr.sizeof_float, input_rate) - sink2 = ra_fft_sink_f (panel, title="Real Data", fft_size=fft_size*2, - sample_rate=input_rate, baseband_freq=100e3, - ref_level=60, y_per_div=10) - vbox.Add (sink2.win, 1, wx.EXPAND) - self.connect (src2, thr2, sink2) - -def main (): - app = stdgui2.stdapp (test_app_flow_graph, - "FFT Sink Test App") - app.MainLoop () - -if __name__ == '__main__': - main () diff --git a/gr-radio-astronomy/src/python/ra_stripchartsink.py b/gr-radio-astronomy/src/python/ra_stripchartsink.py deleted file mode 100755 index 76988f8f0..000000000 --- a/gr-radio-astronomy/src/python/ra_stripchartsink.py +++ /dev/null @@ -1,400 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2003,2004,2005,2007 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. -# - -from gnuradio import gr, gru -from gnuradio.wxgui import stdgui2 -import wx -import gnuradio.wxgui.plot as plot -import Numeric -import threading -import math -import ephem -import time - -default_stripchartsink_size = (640,140) -global_yvalues = [] - -class stripchart_sink_base(object): - def __init__(self, input_is_real=False, y_per_div=10, ref_level=50, - sample_rate=1, stripsize=4, - title='',xlabel="X", ylabel="Y", divbase=0.025, - parallel=False, scaling=1.0, autoscale=False): - - # initialize common attributes - self.y_divs = 8 - self.y_per_div=y_per_div - self.ref_level = ref_level - self.autoscale = autoscale - self.sample_rate = sample_rate - self.parallel = parallel - self.title = title - self.xlabel = xlabel - self.ylabel = ylabel - self.divbase = divbase - self.scaling = scaling - self.input_is_real = input_is_real - self.msgq = gr.msg_queue(2) # queue that holds a maximum of 2 messages - self.vector=Numeric.zeros(stripsize,Numeric.Float64) - self.wcnt = 0 - self.timecnt = 0 - self.stripsize=stripsize - - def set_y_per_div(self, y_per_div): - self.y_per_div = y_per_div - - def set_ref_level(self, ref_level): - self.ref_level = ref_level - - def set_autoscale(self, auto): - self.autoscale = auto - -class stripchart_sink_f(gr.hier_block2, stripchart_sink_base): - def __init__(self, parent, - y_per_div=10, ref_level=50, sample_rate=1, - title='', stripsize=4, - size=default_stripchartsink_size,xlabel="X", - ylabel="Y", divbase=0.025, - parallel=False, scaling=1.0, autoscale=False): - - if parallel == False: - gr.hier_block2.__init__(self, "stripchart_sink_f", - gr.io_signature(1, 1, gr.sizeof_float), - gr.io_signature(0, 0, 0)) - else: - gr.hier_block2.__init__(self, "stripchart_sink_f", - gr.io_signature(1, 1, gr.sizeof_float*stripsize), - gr.io_signature(0, 0, 0)) - - - stripchart_sink_base.__init__(self, input_is_real=True, - y_per_div=y_per_div, ref_level=ref_level, - sample_rate=sample_rate, - stripsize=stripsize, - xlabel=xlabel, ylabel=ylabel, - divbase=divbase, title=title, - parallel=parallel, - scaling=scaling, autoscale=autoscale) - - if (parallel == True): - one = gr.keep_one_in_n (gr.sizeof_float*stripsize, 1) - sink = gr.message_sink(gr.sizeof_float*stripsize, self.msgq, True) - else: - one = gr.keep_one_in_n (gr.sizeof_float, 1) - sink = gr.message_sink(gr.sizeof_float, self.msgq, True) - self.connect (self, one, sink) - - self.win = stripchart_window(self, parent, size=size) - - - -# ------------------------------------------------------------------------ - -myDATA_EVENT = wx.NewEventType() -EVT_DATA_EVENT = wx.PyEventBinder (myDATA_EVENT, 0) - - -class DataEvent(wx.PyEvent): - def __init__(self, data): - wx.PyEvent.__init__(self) - self.SetEventType (myDATA_EVENT) - self.data = data - - def Clone (self): - self.__class__ (self.GetId()) - - -class input_watcher (threading.Thread): - def __init__ (self, msgq, evsize, event_receiver, **kwds): - threading.Thread.__init__ (self, **kwds) - self.setDaemon (1) - self.msgq = msgq - self.evsize = evsize - self.event_receiver = event_receiver - self.keep_running = True - self.start () - - def run (self): - while (self.keep_running): - msg = self.msgq.delete_head() # blocking read of message queue - itemsize = int(msg.arg1()) - nitems = int(msg.arg2()) - - s = msg.to_string() # get the body of the msg as a string - - # There may be more than one frame in the message. - # If so, we take only the last one - if nitems > 1: - start = itemsize * (nitems - 1) - s = s[start:start+itemsize] - - complex_data = Numeric.fromstring (s, Numeric.Float32) - de = DataEvent (complex_data) - wx.PostEvent (self.event_receiver, de) - del de - -class stripchart_window(plot.PlotCanvas): - def __init__ (self, stripchartsink, parent, id = -1, - pos = wx.DefaultPosition, size = wx.DefaultSize, - style = wx.DEFAULT_FRAME_STYLE, name = ""): - plot.PlotCanvas.__init__ (self, parent, id, pos, size, style, name) - - self.y_range = None - self.stripchartsink = stripchartsink - - self.SetEnableGrid (True) - # self.SetEnableZoom (True) - # self.SetBackgroundColour ('black') - - self.build_popup_menu() - - EVT_DATA_EVENT (self, self.set_data) - - wx.EVT_CLOSE (self, self.on_close_window) - self.Bind(wx.EVT_RIGHT_UP, self.on_right_click) - - self.input_watcher = input_watcher(stripchartsink.msgq, 1, self) - - - def on_close_window (self, event): - print "stripchart_window:on_close_window" - self.keep_running = False - - - def set_data (self, evt): - indata = evt.data - L = len (indata) - - calc_min = min(indata) - calc_max = max(indata) - d = calc_max - calc_min - d = d * 0.1 - if self.stripchartsink.autoscale == True and self.stripchartsink.parallel == True: - self.y_range = self._axisInterval ('min', calc_min-d, calc_max+d) - - N = self.stripchartsink.stripsize - if self.stripchartsink.parallel != True: - for i in range(1,N): - pooey = N-i - self.stripchartsink.vector[pooey] = self.stripchartsink.vector[pooey-1] - - self.stripchartsink.vector[0] = indata - - else: - self.stripchartsink.vector = indata - - if self.stripchartsink.parallel == True: - avg = 0 - for i in range(0,self.stripchartsink.stripsize): - if self.stripchartsink.vector[i] > 0: - avg += self.stripchartsink.vector[i] - if self.stripchartsink.vector[i] < calc_min: - calc_min = self.stripchartsink.vector[i] - if self.stripchartsink.vector[i] > calc_max: - calc_max = self.stripchartsink.vector[i] - - avg /= self.stripchartsink.stripsize - markers = [] - placedmarkers = 0 - for i in range(0,self.stripchartsink.stripsize): - if (self.stripchartsink.vector[i] > 0 and - self.stripchartsink.vector[i] > (avg*5)): - markers.append((i*self.stripchartsink.scaling, - self.stripchartsink.vector[i])) - placedmarkers += 1 - - points = Numeric.zeros((N,2), Numeric.Float64) - for i in range(0,N): - if self.stripchartsink.scaling == 1.0: - points[i,0] = i - else: - points[i,0] = i * self.stripchartsink.scaling - points[i,1] = self.stripchartsink.vector[i] - - if self.stripchartsink.parallel == True and placedmarkers > 1: - for i in range(0,N): - self.stripchartsink.vector[i] = 0 - - marks = plot.PolyMarker(markers, colour='BLACK', marker='triangle_down') - - lines = plot.PolyLine (points, colour='RED') - - # Temporary--I'm find the markers distracting - placedmarkers = 0 - xlab = self.stripchartsink.xlabel - ylab = self.stripchartsink.ylabel - if (self.stripchartsink.parallel == False) or (placedmarkers <= 1): - graphics = plot.PlotGraphics ([lines], - title=self.stripchartsink.title, - xLabel = xlab, yLabel = ylab) - - else: - graphics = plot.PlotGraphics ([lines,marks], - title=self.stripchartsink.title, - xLabel = xlab, yLabel = ylab) - - self.Draw (graphics, xAxis=None, yAxis=self.y_range) - - if self.stripchartsink.autoscale == False or self.stripchartsink.parallel == False: - self.update_y_range () - - - def update_y_range (self): - ymax = self.stripchartsink.ref_level - ymin = self.stripchartsink.ref_level - self.stripchartsink.y_per_div * self.stripchartsink.y_divs - self.y_range = self._axisInterval ('min', ymin, ymax) - - def on_incr_ref_level(self, evt): - # print "on_incr_ref_level" - self.stripchartsink.set_ref_level(self.stripchartsink.ref_level - + self.stripchartsink.y_per_div) - - def on_decr_ref_level(self, evt): - # print "on_decr_ref_level" - self.stripchartsink.set_ref_level(self.stripchartsink.ref_level - - self.stripchartsink.y_per_div) - - def on_autoscale(self, evt): - self.stripchartsink.set_autoscale(evt.IsChecked()) - - def on_incr_y_per_div(self, evt): - divbase = self.stripchartsink.divbase - x1 = 1 * divbase - x2 = 2 * divbase - x4 = 4 * divbase - x10 = 10 * divbase - x20 = 20 * divbase - # print "on_incr_y_per_div" - self.stripchartsink.set_y_per_div(next_up(self.stripchartsink.y_per_div, (x1,x2,x4,x10,x20))) - - def on_decr_y_per_div(self, evt): - # print "on_decr_y_per_div" - divbase = self.stripchartsink.divbase - x1 = 1 * divbase - x2 = 2 * divbase - x4 = 4 * divbase - x10 = 10 * divbase - x20 = 20 * divbase - self.stripchartsink.set_y_per_div(next_down(self.stripchartsink.y_per_div, (x1,x2,x4,x10,x20))) - - def on_y_per_div(self, evt): - # print "on_y_per_div" - divbase=self.stripchartsink.divbase - Id = evt.GetId() - if Id == self.id_y_per_div_1: - self.stripchartsink.set_y_per_div(1*divbase) - elif Id == self.id_y_per_div_2: - self.stripchartsink.set_y_per_div(2*divbase) - elif Id == self.id_y_per_div_5: - self.stripchartsink.set_y_per_div(4*divbase) - elif Id == self.id_y_per_div_10: - self.stripchartsink.set_y_per_div(10*divbase) - elif Id == self.id_y_per_div_20: - self.stripchartsink.set_y_per_div(20*divbase) - - - def on_right_click(self, event): - menu = self.popup_menu - for id, pred in self.checkmarks.items(): - item = menu.FindItemById(id) - item.Check(pred()) - self.PopupMenu(menu, event.GetPosition()) - - - def build_popup_menu(self): - divbase=self.stripchartsink.divbase - self.id_incr_ref_level = wx.NewId() - self.id_decr_ref_level = wx.NewId() - self.id_autoscale = wx.NewId() - self.id_incr_y_per_div = wx.NewId() - self.id_decr_y_per_div = wx.NewId() - self.id_y_per_div_1 = wx.NewId() - self.id_y_per_div_2 = wx.NewId() - self.id_y_per_div_5 = wx.NewId() - self.id_y_per_div_10 = wx.NewId() - self.id_y_per_div_20 = wx.NewId() - - self.Bind(wx.EVT_MENU, self.on_incr_ref_level, id=self.id_incr_ref_level) - self.Bind(wx.EVT_MENU, self.on_decr_ref_level, id=self.id_decr_ref_level) - self.Bind(wx.EVT_MENU, self.on_autoscale, id=self.id_autoscale) - self.Bind(wx.EVT_MENU, self.on_incr_y_per_div, id=self.id_incr_y_per_div) - self.Bind(wx.EVT_MENU, self.on_decr_y_per_div, id=self.id_decr_y_per_div) - self.Bind(wx.EVT_MENU, self.on_y_per_div, id=self.id_y_per_div_1) - self.Bind(wx.EVT_MENU, self.on_y_per_div, id=self.id_y_per_div_2) - self.Bind(wx.EVT_MENU, self.on_y_per_div, id=self.id_y_per_div_5) - self.Bind(wx.EVT_MENU, self.on_y_per_div, id=self.id_y_per_div_10) - self.Bind(wx.EVT_MENU, self.on_y_per_div, id=self.id_y_per_div_20) - - - # make a menu - menu = wx.Menu() - self.popup_menu = menu - menu.Append(self.id_incr_ref_level, "Incr Ref Level") - menu.Append(self.id_decr_ref_level, "Decr Ref Level") - menu.AppendSeparator() - menu.AppendCheckItem(self.id_autoscale, "Auto Scale") - # we'd use RadioItems for these, but they're not supported on Mac - v = 1.0*divbase - s = "%.3f" % v - menu.AppendCheckItem(self.id_y_per_div_1, s) - v = 2.0*divbase - s = "%.3f" % v - menu.AppendCheckItem(self.id_y_per_div_2, s) - v = 4.0*divbase - s = "%.3f" % v - menu.AppendCheckItem(self.id_y_per_div_5, s) - v = 10*divbase - s = "%.3f" % v - menu.AppendCheckItem(self.id_y_per_div_10, s) - v = 20*divbase - s = "%.3f" % v - menu.AppendCheckItem(self.id_y_per_div_20, s) - - self.checkmarks = { - self.id_autoscale : lambda : self.stripchartsink.autoscale, - self.id_y_per_div_1 : lambda : self.stripchartsink.y_per_div == 1*divbase, - self.id_y_per_div_2 : lambda : self.stripchartsink.y_per_div == 2*divbase, - self.id_y_per_div_5 : lambda : self.stripchartsink.y_per_div == 4*divbase, - self.id_y_per_div_10 : lambda : self.stripchartsink.y_per_div == 10*divbase, - self.id_y_per_div_20 : lambda : self.stripchartsink.y_per_div == 20*divbase, - } - - -def next_up(v, seq): - """ - Return the first item in seq that is > v. - """ - for s in seq: - if s > v: - return s - return v - -def next_down(v, seq): - """ - Return the last item in seq that is < v. - """ - rseq = list(seq[:]) - rseq.reverse() - - for s in rseq: - if s < v: - return s - return v diff --git a/gr-radio-astronomy/src/python/ra_waterfallsink.py b/gr-radio-astronomy/src/python/ra_waterfallsink.py deleted file mode 100755 index e946f3a10..000000000 --- a/gr-radio-astronomy/src/python/ra_waterfallsink.py +++ /dev/null @@ -1,646 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2003,2004,2005,2007 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 2, 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. -# - -from gnuradio import gr, gru, window -from gnuradio.wxgui import stdgui2 -import wx -import gnuradio.wxgui.plot as plot -import numpy -import os -import threading -import math - -default_fftsink_size = (640,240) -default_fft_rate = gr.prefs().get_long('wxgui', 'fft_rate', 15) - -def axis_design( x1, x2, nx ): - # Given start, end, and number of labels, return value of first label, - # increment between labels, number of unlabeled division between labels, - # and scale factor. - - dx = abs( x2 - x1 )/float(nx+1) # allow for space at each end - ldx = math.log10(dx) - l2 = math.log10(2.) - l5 = math.log10(5.) - le = math.floor(ldx) - lf = ldx - le - if lf < l2/2: - c = 1 - dt = 10 - elif lf < (l2+l5)/2: - c = 2 - dt = 4 - elif lf < (l5+1)/2: - c = 5 - dt = 5 - else: - c = 1 - dt = 10 - le += 1 - inc = c*pow( 10., le ) - first = math.ceil( x1/inc )*inc - scale = 1. - while ( abs(x1*scale) >= 1e5 ) or ( abs(x2*scale) >= 1e5 ): - scale *= 1e-3 - return ( first, inc, dt, scale ) - - -class waterfall_sink_base(object): - def __init__(self, input_is_real=False, baseband_freq=0, - sample_rate=1, fft_size=512, - fft_rate=default_fft_rate, - average=False, avg_alpha=None, title='', ofunc=None, xydfunc=None): - - # initialize common attributes - self.baseband_freq = baseband_freq - self.sample_rate = sample_rate - self.fft_size = fft_size - self.fft_rate = fft_rate - self.average = average - self.ofunc = ofunc - self.xydfunc = xydfunc - if avg_alpha is None: - self.avg_alpha = 2.0 / fft_rate - else: - self.avg_alpha = avg_alpha - self.title = title - self.input_is_real = input_is_real - self.msgq = gr.msg_queue(2) # queue up to 2 messages - - def set_average(self, average): - self.average = average - if average: - self.avg.set_taps(self.avg_alpha) - else: - self.avg.set_taps(1.0) - - def set_avg_alpha(self, avg_alpha): - self.avg_alpha = avg_alpha - - def set_baseband_freq(self, baseband_freq): - self.baseband_freq = baseband_freq - - def set_sample_rate(self, sample_rate): - self.sample_rate = sample_rate - self._set_n() - - def _set_n(self): - self.one_in_n.set_n(max(1, int(self.sample_rate/self.fft_size/self.fft_rate))) - -class waterfall_sink_f(gr.hier_block2, waterfall_sink_base): - def __init__(self, parent, baseband_freq=0, - ref_level=0, sample_rate=1, fft_size=512, - fft_rate=default_fft_rate, average=False, avg_alpha=None, - title='', size=default_fftsink_size, report=None, span=40, ofunc=None, xydfunc=None): - - gr.hier_block2.__init__(self, "waterfall_sink_f", - gr.io_signature(1, 1, gr.sizeof_float), - gr.io_signature(0, 0, 0)) - - waterfall_sink_base.__init__(self, input_is_real=True, - baseband_freq=baseband_freq, - sample_rate=sample_rate, - fft_size=fft_size, fft_rate=fft_rate, - average=average, avg_alpha=avg_alpha, - title=title) - - s2p = gr.serial_to_parallel(gr.sizeof_float, self.fft_size) - self.one_in_n = gr.keep_one_in_n(gr.sizeof_float * self.fft_size, - max(1, int(self.sample_rate/self.fft_size/self.fft_rate))) - mywindow = window.blackmanharris(self.fft_size) - fft = gr.fft_vfc(self.fft_size, True, mywindow) - c2mag = gr.complex_to_mag(self.fft_size) - self.avg = gr.single_pole_iir_filter_ff(1.0, self.fft_size) - log = gr.nlog10_ff(20, self.fft_size, -20*math.log10(self.fft_size)) - sink = gr.message_sink(gr.sizeof_float * self.fft_size, self.msgq, True) - - self.connect(self, s2p, self.one_in_n, fft, c2mag, self.avg, log, sink) - self.win = waterfall_window(self, parent, size=size, report=report, - ref_level=ref_level, span=span, ofunc=ofunc, xydfunc=xydfunc) - self.set_average(self.average) - - -class waterfall_sink_c(gr.hier_block2, waterfall_sink_base): - def __init__(self, parent, baseband_freq=0, - ref_level=0, sample_rate=1, fft_size=512, - fft_rate=default_fft_rate, average=False, avg_alpha=None, - title='', size=default_fftsink_size, report=None, span=40, ofunc=None, xydfunc=None): - - gr.hier_block2.__init__(self, "waterfall_sink_c", - gr.io_signature(1, 1, gr.sizeof_gr_complex), - gr.io_signature(0, 0, 0)) - - waterfall_sink_base.__init__(self, input_is_real=False, - baseband_freq=baseband_freq, - sample_rate=sample_rate, - fft_size=fft_size, - fft_rate=fft_rate, - average=average, avg_alpha=avg_alpha, - title=title) - - s2p = gr.serial_to_parallel(gr.sizeof_gr_complex, self.fft_size) - self.one_in_n = gr.keep_one_in_n(gr.sizeof_gr_complex * self.fft_size, - max(1, int(self.sample_rate/self.fft_size/self.fft_rate))) - - mywindow = window.blackmanharris(self.fft_size) - fft = gr.fft_vcc(self.fft_size, True, mywindow) - c2mag = gr.complex_to_mag(self.fft_size) - self.avg = gr.single_pole_iir_filter_ff(1.0, self.fft_size) - log = gr.nlog10_ff(20, self.fft_size, -20*math.log10(self.fft_size)) - sink = gr.message_sink(gr.sizeof_float * self.fft_size, self.msgq, True) - - self.connect(self, s2p, self.one_in_n, fft, c2mag, self.avg, log, sink) - self.win = waterfall_window(self, parent, size=size, report=report, - ref_level=ref_level, span=span, ofunc=ofunc, xydfunc=xydfunc) - self.set_average(self.average) - - -# ------------------------------------------------------------------------ - -myDATA_EVENT = wx.NewEventType() -EVT_DATA_EVENT = wx.PyEventBinder (myDATA_EVENT, 0) - - -class DataEvent(wx.PyEvent): - def __init__(self, data): - wx.PyEvent.__init__(self) - self.SetEventType (myDATA_EVENT) - self.data = data - - def Clone (self): - self.__class__ (self.GetId()) - - -class input_watcher (threading.Thread): - def __init__ (self, msgq, fft_size, event_receiver, **kwds): - threading.Thread.__init__ (self, **kwds) - self.setDaemon (1) - self.msgq = msgq - self.fft_size = fft_size - self.event_receiver = event_receiver - self.keep_running = True - self.start () - - def run (self): - while (self.keep_running): - msg = self.msgq.delete_head() # blocking read of message queue - itemsize = int(msg.arg1()) - nitems = int(msg.arg2()) - - s = msg.to_string() # get the body of the msg as a string - - # There may be more than one FFT frame in the message. - # If so, we take only the last one - if nitems > 1: - start = itemsize * (nitems - 1) - s = s[start:start+itemsize] - - complex_data = numpy.fromstring (s, numpy.float32) - de = DataEvent (complex_data) - wx.PostEvent (self.event_receiver, de) - del de - - -class waterfall_window (wx.ScrolledWindow): - def __init__ (self, fftsink, parent, id = -1, - pos = wx.DefaultPosition, size = wx.DefaultSize, - style = wx.DEFAULT_FRAME_STYLE, name = "", report=None, - ref_level = 0, span = 50, ofunc=None, xydfunc=None): - wx.ScrolledWindow.__init__(self, parent, id, pos, size, - style|wx.HSCROLL, name) - self.parent = parent - self.SetCursor(wx.StockCursor(wx.CURSOR_IBEAM)) - self.ref_level = ref_level - self.scale_factor = 256./span - - self.ppsh = 128 # pixels per scroll, horizontal - self.SetScrollbars( self.ppsh, 0, fftsink.fft_size/self.ppsh, 0 ) - - self.fftsink = fftsink - self.size = size - self.report = report - self.ofunc = ofunc - self.xydfunc = xydfunc - - dc1 = wx.MemoryDC() - dc1.SetFont( wx.SMALL_FONT ) - self.h_scale = dc1.GetCharHeight() + 3 - #self.bm_size = ( self.fftsink.fft_size, self.size[1] - self.h_scale ) - self.im_size = ( self.fftsink.fft_size, self.size[1] - self.h_scale ) - #self.bm = wx.EmptyBitmap( self.bm_size[0], self.bm_size[1], -1) - self.im = wx.EmptyImage( self.im_size[0], self.im_size[1], True ) - self.im_cur = 0 - - self.baseband_freq = None - - self.make_pens() - - wx.EVT_PAINT( self, self.OnPaint ) - wx.EVT_CLOSE (self, self.on_close_window) - #wx.EVT_LEFT_UP(self, self.on_left_up) - #wx.EVT_LEFT_DOWN(self, self.on_left_down) - EVT_DATA_EVENT (self, self.set_data) - - self.build_popup_menu() - - wx.EVT_CLOSE (self, self.on_close_window) - self.Bind(wx.EVT_RIGHT_UP, self.on_right_click) - self.Bind(wx.EVT_MOTION, self.on_motion) - - self.down_pos = None - - self.input_watcher = input_watcher(fftsink.msgq, fftsink.fft_size, self) - - def on_close_window (self, event): - self.keep_running = False - - def on_left_down( self, evt ): - self.down_pos = evt.GetPosition() - self.down_time = evt.GetTimestamp() - - def on_left_up( self, evt ): - if self.down_pos: - dt = ( evt.GetTimestamp() - self.down_time )/1000. - pph = self.fftsink.fft_size/float(self.fftsink.sample_rate) - dx = evt.GetPosition()[0] - self.down_pos[0] - if dx != 0: - rt = pph/dx - else: - rt = 0 - t = 'Down time: %f Delta f: %f Period: %f' % ( dt, dx/pph, rt ) - print t - if self.report: - self.report(t) - - def on_motion(self, event): - if self.xydfunc: - pos = event.GetPosition() - self.xydfunc(pos) - - - def const_list(self,const,len): - return [const] * len - - def make_colormap(self): - r = [] - r.extend(self.const_list(0,96)) - r.extend(range(0,255,4)) - r.extend(self.const_list(255,64)) - r.extend(range(255,128,-4)) - - g = [] - g.extend(self.const_list(0,32)) - g.extend(range(0,255,4)) - g.extend(self.const_list(255,64)) - g.extend(range(255,0,-4)) - g.extend(self.const_list(0,32)) - - b = range(128,255,4) - b.extend(self.const_list(255,64)) - b.extend(range(255,0,-4)) - b.extend(self.const_list(0,96)) - return (r,g,b) - - def make_pens(self): - (r,g,b) = self.make_colormap() - self.rgb = numpy.transpose( numpy.array( (r,g,b) ).astype(numpy.int8) ) - - def OnPaint(self, event): - dc = wx.BufferedPaintDC(self) - self.DoDrawing( dc ) - - def DoDrawing(self,dc): - w, h = self.GetClientSizeTuple() - w = min( w, self.fftsink.fft_size ) - if w <= 0: - return - - if dc is None: - dc = wx.BufferedDC( wx.ClientDC(self), (w,h) ) - - dc.SetBackground( wx.Brush( self.GetBackgroundColour(), wx.SOLID ) ) - dc.Clear() - - x, y = self.GetViewStart() - x *= self.ppsh - - ih = min( h - self.h_scale, self.im_size[1] - self.im_cur ) - r = wx.Rect( x, self.im_cur, w, ih ) - bm = wx.BitmapFromImage( self.im.GetSubImage(r) ) - dc.DrawBitmap( bm, 0, self.h_scale ) - rem = min( self.im_size[1] - ih, h - ih - self.h_scale ) - if( rem > 0 ): - r = wx.Rect( x, 0, w, rem ) - bm = wx.BitmapFromImage( self.im.GetSubImage(r) ) - dc.DrawBitmap( bm, 0, ih + self.h_scale ) - - # Draw axis - if self.baseband_freq != self.fftsink.baseband_freq: - self.baseband_freq = self.fftsink.baseband_freq - t = self.fftsink.sample_rate*w/float(self.fftsink.fft_size) - self.ax_spec = axis_design( self.baseband_freq - t/2, - self.baseband_freq + t/2, 7 ) - dc.SetFont( wx.SMALL_FONT ) - fo = self.baseband_freq - po = self.fftsink.fft_size/2 - pph = self.fftsink.fft_size/float(self.fftsink.sample_rate) - f = math.floor((fo-po/pph)/self.ax_spec[1])*self.ax_spec[1] - while True: - t = po + ( f - fo )*pph - s = str( f*self.ax_spec[3] ) - e = dc.GetTextExtent( s ) - if t - e[1]/2 >= x + w: - break - dc.DrawText( s, t - x - e[0]/2, 0 ) - dc.DrawLine( t - x, e[1] - 1, t - x, self.h_scale ) - dt = self.ax_spec[1]/self.ax_spec[2]*pph - for i in range(self.ax_spec[2]-1): - t += dt - if t >= x + w: - break - dc.DrawLine( t - x, e[1] + 1, t - x, self.h_scale ) - f += self.ax_spec[1] - - def const_list(self,const,len): - a = [const] - for i in range(1,len): - a.append(const) - return a - - def make_colormap(self): - r = [] - r.extend(self.const_list(0,96)) - r.extend(range(0,255,4)) - r.extend(self.const_list(255,64)) - r.extend(range(255,128,-4)) - - g = [] - g.extend(self.const_list(0,32)) - g.extend(range(0,255,4)) - g.extend(self.const_list(255,64)) - g.extend(range(255,0,-4)) - g.extend(self.const_list(0,32)) - - b = range(128,255,4) - b.extend(self.const_list(255,64)) - b.extend(range(255,0,-4)) - b.extend(self.const_list(0,96)) - return (r,g,b) - - def set_data (self, evt): - dB = evt.data - L = len (dB) - - if self.ofunc != None: - self.ofunc(evt.data, L) - #dc1 = wx.MemoryDC() - #dc1.SelectObject(self.bm) - - # Scroll existing bitmap - if 1: - #dc1.Blit(0,1,self.bm_size[0],self.bm_size[1]-1,dc1,0,0, - # wx.COPY,False,-1,-1) - pass - else: - for i in range( self.bm_size[1]-1, 0, -1 ): - dc1.Blit( 0, i, self.bm_size[0], 1, dc1, 0, i-1 ) - - x = max(abs(self.fftsink.sample_rate), abs(self.fftsink.baseband_freq)) - if x >= 1e9: - sf = 1e-9 - units = "GHz" - elif x >= 1e6: - sf = 1e-6 - units = "MHz" - else: - sf = 1e-3 - units = "kHz" - - - if self.fftsink.input_is_real: # only plot 1/2 the points - d_max = L/2 - p_width = 2 - else: - d_max = L/2 - p_width = 1 - - scale_factor = self.scale_factor - dB -= self.ref_level - dB *= scale_factor - dB = dB.astype(numpy.int_).clip( min=0, max=255 ) - if self.fftsink.input_is_real: # real fft - dB = numpy.array( ( dB[0:d_max][::-1], dB[0:d_max] ) ) - else: # complex fft - dB = numpy.concatenate( ( dB[d_max:L], dB[0:d_max] ) ) - - dB = self.rgb[dB] - img = wx.ImageFromData( L, 1, dB.ravel().tostring() ) - #bm = wx.BitmapFromImage( img ) - #dc1.DrawBitmap( bm, 0, 0 ) - ibuf = self.im.GetDataBuffer() - self.im_cur -= 1 - if self.im_cur < 0: - self.im_cur = self.im_size[1] - 1 - start = 3*self.im_cur*self.im_size[0] - ibuf[start:start+3*self.im_size[0]] = img.GetData() - - #del dc1 - self.DoDrawing(None) - - def on_average(self, evt): - # print "on_average" - self.fftsink.set_average(evt.IsChecked()) - - def on_right_click(self, event): - menu = self.popup_menu - self.PopupMenu(menu, event.GetPosition()) - - - def build_popup_menu(self): - id_ref_gain = wx.NewId() - self.Bind( wx.EVT_MENU, self.on_ref_gain, id=id_ref_gain ) - - # make a menu - menu = wx.Menu() - self.popup_menu = menu - menu.Append( id_ref_gain, "Ref Level and Gain" ) - self.rg_dialog = None - - self.checkmarks = { - #self.id_average : lambda : self.fftsink.average - } - - def on_ref_gain( self, evt ): - if self.rg_dialog == None: - self.rg_dialog = rg_dialog( self.parent, self.set_ref_gain, - ref=self.ref_level, - span=256./self.scale_factor ) - self.rg_dialog.Show( True ) - - def set_ref_gain( self, ref, span ): - self.ref_level = ref - self.scale_factor = 256/span - -class rg_dialog( wx.Dialog ): - def __init__( self, parent, set_function, ref=0, span=256./5. ): - wx.Dialog.__init__( self, parent, -1, "Waterfall Settings" ) - self.set_function = set_function - #status_bar = wx.StatusBar( self, -1 ) - - d_sizer = wx.BoxSizer( wx.VERTICAL ) # dialog sizer - f_sizer = wx.BoxSizer( wx.VERTICAL ) # form sizer - vs = 10 - - #f_sizer.Add( fn_sizer, 0, flag=wx.TOP, border=10 ) - - h_sizer = wx.BoxSizer( wx.HORIZONTAL ) - self.ref = tab_item( self, "Ref Level:", 4, "dB" ) - self.ref.ctrl.SetValue( "%d" % ref ) - h_sizer.Add((0,0),1) - h_sizer.Add( self.ref, 0 ) - h_sizer.Add((0,0),1) - self.span = tab_item( self, "Range:", 4, "dB" ) - self.span.ctrl.SetValue( "%d" % span ) - h_sizer.Add( self.span, 0 ) - h_sizer.Add((0,0),1) - f_sizer.Add( h_sizer, 0, flag=wx.TOP|wx.EXPAND, border=vs ) - - d_sizer.Add((0,0),1) - d_sizer.Add( f_sizer, 0, flag=wx.ALIGN_CENTER_HORIZONTAL|wx.EXPAND ) - d_sizer.Add((0,0),1) - d_sizer.Add((0,0),1) - - button_sizer = wx.BoxSizer( wx.HORIZONTAL ) - apply_button = wx.Button( self, -1, "Apply" ) - apply_button.Bind( wx.EVT_BUTTON, self.apply_evt ) - cancel_button = wx.Button( self, -1, "Cancel" ) - cancel_button.Bind( wx.EVT_BUTTON, self.cancel_evt ) - ok_button = wx.Button( self, -1, "OK" ) - ok_button.Bind( wx.EVT_BUTTON, self.ok_evt ) - button_sizer.Add((0,0),1) - button_sizer.Add( apply_button, 0, - flag=wx.ALIGN_CENTER_HORIZONTAL ) - button_sizer.Add((0,0),1) - button_sizer.Add( cancel_button, 0, - flag=wx.ALIGN_CENTER_HORIZONTAL ) - button_sizer.Add((0,0),1) - button_sizer.Add( ok_button, 0, - flag=wx.ALIGN_CENTER_HORIZONTAL ) - button_sizer.Add((0,0),1) - d_sizer.Add( button_sizer, 0, - flag=wx.EXPAND|wx.ALIGN_CENTER|wx.BOTTOM, border=30 ) - self.SetSizer( d_sizer ) - - def apply_evt( self, evt ): - self.do_apply() - - def cancel_evt( self, evt ): - self.Show( False ) - - def ok_evt( self, evt ): - self.do_apply() - self.Show( False ) - - def do_apply( self ): - r = float( self.ref.ctrl.GetValue() ) - g = float( self.span.ctrl.GetValue() ) - self.set_function( r, g ) - -def next_up(v, seq): - """ - Return the first item in seq that is > v. - """ - for s in seq: - if s > v: - return s - return v - -def next_down(v, seq): - """ - Return the last item in seq that is < v. - """ - rseq = list(seq[:]) - rseq.reverse() - - for s in rseq: - if s < v: - return s - return v - -# One of many copies that should be consolidated . . . -def tab_item( parent, label, chars, units, style=wx.TE_RIGHT, value="" ): - s = wx.BoxSizer( wx.HORIZONTAL ) - s.Add( wx.StaticText( parent, -1, label ), 0, - flag=wx.ALIGN_CENTER_VERTICAL ) - s.ctrl = wx.TextCtrl( parent, -1, style=style, value=value ) - s.ctrl.SetMinSize( ( (1.00+chars)*s.ctrl.GetCharWidth(), - 1.25*s.ctrl.GetCharHeight() ) ) - s.Add( s.ctrl, -1, flag=wx.LEFT, border=3 ) - s.Add( wx.StaticText( parent, -1, units ), 0, - flag=wx.ALIGN_CENTER_VERTICAL|wx.LEFT, border=1 ) - return s - - -# ---------------------------------------------------------------- -# Standalone test app -# ---------------------------------------------------------------- - -class test_app_flow_graph (stdgui2.std_top_block): - def __init__(self, frame, panel, vbox, argv): - stdgui2.std_top_block.__init__ (self, frame, panel, vbox, argv) - - fft_size = 512 - - # build our flow graph - input_rate = 20.000e3 - - # Generate a complex sinusoid - src1 = gr.sig_source_c (input_rate, gr.GR_SIN_WAVE, 5.75e3, 1000) - #src1 = gr.sig_source_c (input_rate, gr.GR_CONST_WAVE, 5.75e3, 1000) - - # We add these throttle blocks so that this demo doesn't - # suck down all the CPU available. Normally you wouldn't use these. - thr1 = gr.throttle(gr.sizeof_gr_complex, input_rate) - - sink1 = waterfall_sink_c (panel, title="Complex Data", - fft_size=fft_size, - sample_rate=input_rate, baseband_freq=0, - size=(600,144) ) - vbox.Add (sink1.win, 1, wx.EXPAND) - self.connect (src1, thr1, sink1) - - # generate a real sinusoid - src2 = gr.sig_source_f (input_rate, gr.GR_SIN_WAVE, 5.75e3, 1000) - #src2 = gr.sig_source_f (input_rate, gr.GR_CONST_WAVE, 5.75e3, 1000) - thr2 = gr.throttle(gr.sizeof_float, input_rate) - sink2 = waterfall_sink_f (panel, title="Real Data", fft_size=fft_size, - sample_rate=input_rate, baseband_freq=0) - vbox.Add (sink2.win, 1, wx.EXPAND) - self.connect (src2, thr2, sink2) - -def main (): - app = stdgui2.stdapp (test_app_flow_graph, - "Waterfall Sink Test App") - app.MainLoop () - -if __name__ == '__main__': - main () diff --git a/gr-radio-astronomy/src/python/run_tests.in b/gr-radio-astronomy/src/python/run_tests.in deleted file mode 100644 index c382145a6..000000000 --- a/gr-radio-astronomy/src/python/run_tests.in +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/sh - -# 1st parameter is absolute path to component source directory -# 2nd parameter is absolute path to component build directory -# 3rd parameter is path to Python QA directory - -@top_builddir@/run_tests.sh \ - @abs_top_srcdir@/gr-radio-astronomy \ - @abs_top_builddir@/gr-radio-astronomy \ - @srcdir@ diff --git a/gr-radio-astronomy/src/python/usrp_psr_receiver.help b/gr-radio-astronomy/src/python/usrp_psr_receiver.help deleted file mode 100644 index 5801f3fbb..000000000 --- a/gr-radio-astronomy/src/python/usrp_psr_receiver.help +++ /dev/null @@ -1,111 +0,0 @@ -This program is used to analyse pulsars of known parameters. It contains - both a post-detector spectral display, and a "pulse profile" display. - It has a built-in de-dispersion filter that will work up to DM=100 for - 21cm observing, and up to DM=5 for 327Mhz observing. - -The program takes the following options: - - --rx-subdev-spec which USRP Rx side? A or B - - --decim USRP decimation rate use either 64 or 128 - - --freq USRP daughtercard frequency - - --observing Actual observing frequency (default is to use the - setting for --freq) - - --avg Averaging setting for spectral display--higher numbers - equal more averaging. 25 to 40 is typical. - - --favg Pulse folding averaging. 2 to 5 is typical. - - --gain USRP daughtercard gain control - - --reflevel Reference level on pulse profile display - - --lowest Lowest spectral bin that is considered valid, in Hz - - --longitude Observer longitude: West is negative - - --latitude Observer latitude: South is negative - - --fft_size Size of FFT for post-detector spectrum: default is 1024 - - --threshold Threshold (dB) to be considered a spectral "peak" - This is relative to the average spectral level - - --lowpass Low pass frequency for post-detector spectral display - 20-100 is typical - - --prefix Filename prefix to use for recording files - Default is ./ - - --pulsefreq The frequency of the expected pulses - For sentimental reasons, this defaults to 0.748Hz - - --dm The DM - - --doppler The doppler shift, as a ratio - - --divbase The base of the Y/Div menu in pulsar display - - --division The initial Y/Div in pulsar display - -DM, Doppler, Gain, Frequency, and the averaging parameters can all be - changed using the GUI at runtime. - -If latitude and longitude are set correctly, and the system time is - correct, then the current LMST is displayed below the frequency - input, updated once per second. - -Moving the mouse in the post-detector spectrum display shows you that - point in the post-detector spectrum, both frequency and signal level. - -The post-detector spectrum is analysed, with results shown below - "Best freq". It shows the spectral peaks, and computes their relationship. - It shows the harmonic compliance among the peaks, as well as the average - peak-to-peak distance. - - -Here's a complete example for observing a pulsar with a frequency of - 1.35Hz, at 431.5Mhz, using an IF of 10.7Mhz, and a DM of 12.431, using - 1Mhz observing bandwidth: - -./usrp_psr_receiver.py --freq 10.7e6 --decim 64 --dm 12.431 --avg 35 \ - --pulsefreq 1.35 --fft_size 2048 --lowest 1.00 --gain 75 --threshold 11.5 \ - --observing 431.5e6 --reflevel 200 --division 100 --divbase 10 --favg 3 \ - --lowpass 20 --longitude -76.02 --latitude 44.95 - -Since the observed pulsar is at 1.35Hz, a lowpass cutoff for the - post-detector spectral display of 20Hz will be adequate. We - tell the spectral analyser to use a threshold of 11.5dB above - average when analysing spectral data, and set the epoch folder - averager (pulse profile display) to use an average from 3 samples. - Notice that our actual USRP/Daughtercard frequency is 10.7Mhz, while - our observing frequency is 431.5Mhz--this is important in order for - the DM de-dispersion calculations to be correct. We also set our - latitude and longitude, so that logfiles and the LMST display - will have the correct LMST in them. - -The entire complex baseband can be recorded, if the "Recording baseband" - button is pressed. Filenames are generated dynamically, and a header - file is produced giving observation parameters. The baseband data are - recorded as octet pairs: one for I and one for Q. Pressing the button again - turns off baseband recording. This baseband is "raw", so it will - not have been de-dispersed. The data rate will be whatever the - USRP was programmed to at the time (based on --decim). - - The files are: YYYYMMDDHHMM.pdat and YYYYMMDDHHMM.phdr - - The .phdr file contains ASCII header information describing the - contents of the .pdat file. - -Similarly the raw, pre-folded, band-limited post-detector "audio" data can be - recorded using the "Record Pulses" button. The data rate for these is - currently 20Khz, recorded as short integers. Just like baseband recording, - pressing the button again turns off pulse recording. - - The files are: YYYYMMDDHHMM.padat and YYMMDDHHMM.pahdr - - The .pahdr file is ascii text providing information about the contents - of the corresponding .padat file. diff --git a/gr-radio-astronomy/src/python/usrp_psr_receiver.py b/gr-radio-astronomy/src/python/usrp_psr_receiver.py deleted file mode 100755 index 6ce4325a2..000000000 --- a/gr-radio-astronomy/src/python/usrp_psr_receiver.py +++ /dev/null @@ -1,1096 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2004,2005,2007 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. -# - - -# -# -# Pulsar receiver application -# -# Performs both harmonic folding analysis -# and epoch folding analysis -# -# -from gnuradio import gr, gru, blks2, audio -from usrpm import usrp_dbid -from gnuradio import usrp, optfir -from gnuradio import eng_notation -from gnuradio.eng_option import eng_option -from gnuradio.wxgui import stdgui2, ra_fftsink, ra_stripchartsink, form, slider -from optparse import OptionParser -import wx -import sys -import Numeric -import numpy.fft -import ephem -import time -import os -import math - - -class app_flow_graph(stdgui2.std_top_block): - def __init__(self, frame, panel, vbox, argv): - stdgui2.std_top_block.__init__(self, frame, panel, vbox, argv) - - self.frame = frame - self.panel = panel - - parser = OptionParser(option_class=eng_option) - parser.add_option("-R", "--rx-subdev-spec", type="subdev", default=(0, 0), - help="select USRP Rx side A or B (default=A)") - parser.add_option("-d", "--decim", type="int", default=16, - help="set fgpa decimation rate to DECIM [default=%default]") - parser.add_option("-f", "--freq", type="eng_float", default=None, - help="set frequency to FREQ", metavar="FREQ") - parser.add_option("-Q", "--observing", type="eng_float", default=0.0, - help="set observing frequency to FREQ") - parser.add_option("-a", "--avg", type="eng_float", default=1.0, - help="set spectral averaging alpha") - parser.add_option("-V", "--favg", type="eng_float", default=2.0, - help="set folder averaging alpha") - parser.add_option("-g", "--gain", type="eng_float", default=None, - help="set gain in dB (default is midpoint)") - parser.add_option("-l", "--reflevel", type="eng_float", default=30.0, - help="Set pulse display reference level") - parser.add_option("-L", "--lowest", type="eng_float", default=1.5, - help="Lowest valid frequency bin") - parser.add_option("-e", "--longitude", type="eng_float", default=-76.02, help="Set Observer Longitude") - parser.add_option("-c", "--latitude", type="eng_float", default=44.85, help="Set Observer Latitude") - parser.add_option("-F", "--fft_size", type="eng_float", default=1024, help="Size of FFT") - - parser.add_option ("-t", "--threshold", type="eng_float", default=2.5, help="pulsar threshold") - parser.add_option("-p", "--lowpass", type="eng_float", default=100, help="Pulse spectra cutoff freq") - parser.add_option("-P", "--prefix", default="./", help="File prefix") - parser.add_option("-u", "--pulsefreq", type="eng_float", default=0.748, help="Observation pulse rate") - parser.add_option("-D", "--dm", type="eng_float", default=1.0e-5, help="Dispersion Measure") - parser.add_option("-O", "--doppler", type="eng_float", default=1.0, help="Doppler ratio") - parser.add_option("-B", "--divbase", type="eng_float", default=20, help="Y/Div menu base") - parser.add_option("-I", "--division", type="eng_float", default=100, help="Y/Div") - parser.add_option("-A", "--audio_source", default="plughw:0,0", help="Audio input device spec") - parser.add_option("-N", "--num_pulses", default=1, type="eng_float", help="Number of display pulses") - (options, args) = parser.parse_args() - if len(args) != 0: - parser.print_help() - sys.exit(1) - - self.show_debug_info = True - - self.reflevel = options.reflevel - self.divbase = options.divbase - self.division = options.division - self.audiodev = options.audio_source - self.mult = int(options.num_pulses) - - # Low-pass cutoff for post-detector filter - # Set to 100Hz usually, since lots of pulsars fit in this - # range - self.lowpass = options.lowpass - - # What is lowest valid frequency bin in post-detector FFT? - # There's some pollution very close to DC - self.lowest_freq = options.lowest - - # What (dB) threshold to use in determining spectral candidates - self.threshold = options.threshold - - # Filename prefix for recording file - self.prefix = options.prefix - - # Dispersion Measure (DM) - self.dm = options.dm - - # Doppler shift, as a ratio - # 1.0 == no doppler shift - # 1.005 == a little negative shift - # 0.995 == a little positive shift - self.doppler = options.doppler - - # - # Input frequency and observing frequency--not necessarily the - # same thing, if we're looking at the IF of some downconverter - # that's ahead of the USRP and daughtercard. This distinction - # is important in computing the correct de-dispersion filter. - # - self.frequency = options.freq - if options.observing <= 0: - self.observing_freq = options.freq - else: - self.observing_freq = options.observing - - # build the graph - self.u = usrp.source_c(decim_rate=options.decim) - self.u.set_mux(usrp.determine_rx_mux_value(self.u, options.rx_subdev_spec)) - - # - # Recording file, in case we ever need to record baseband data - # - self.recording = gr.file_sink(gr.sizeof_char, "/dev/null") - self.recording_state = False - - self.pulse_recording = gr.file_sink(gr.sizeof_short, "/dev/null") - self.pulse_recording_state = False - - # - # We come up with recording turned off, but the user may - # request recording later on - self.recording.close() - self.pulse_recording.close() - - # - # Need these two for converting 12-bit baseband signals to 8-bit - # - self.tofloat = gr.complex_to_float() - self.tochar = gr.float_to_char() - - # Need this for recording pulses (post-detector) - self.toshort = gr.float_to_short() - - - # - # The spectral measurer sets this when it has a valid - # average spectral peak-to-peak distance - # We can then use this to program the parameters for the epoch folder - # - # We set a sentimental value here - self.pulse_freq = options.pulsefreq - - # Folder runs at this raw sample rate - self.folder_input_rate = 20000 - - # Each pulse in the epoch folder is sampled at 128 times the nominal - # pulse rate - self.folding = 128 - - - # - # Try to find candidate parameters for rational resampler - # - save_i = 0 - candidates = [] - for i in range(20,300): - input_rate = self.folder_input_rate - output_rate = int(self.pulse_freq * i) - interp = gru.lcm(input_rate, output_rate) / input_rate - decim = gru.lcm(input_rate, output_rate) / output_rate - if (interp < 500 and decim < 250000): - candidates.append(i) - - # We didn't find anything, bail! - if (len(candidates) < 1): - print "Couldn't converge on resampler parameters" - sys.exit(1) - - # - # Now try to find candidate with the least sampling error - # - mindiff = 999.999 - for i in candidates: - diff = self.pulse_freq * i - diff = diff - int(diff) - if (diff < mindiff): - mindiff = diff - save_i = i - - # Recompute rates - input_rate = self.folder_input_rate - output_rate = int(self.pulse_freq * save_i) - - # Compute new interp and decim, based on best candidate - interp = gru.lcm(input_rate, output_rate) / input_rate - decim = gru.lcm(input_rate, output_rate) / output_rate - - # Save optimized folding parameters, used later - self.folding = save_i - self.interp = int(interp) - self.decim = int(decim) - - # So that we can view N pulses in the pulse viewer window - FOLD_MULT=self.mult - - # determine the daughterboard subdevice we're using - self.subdev = usrp.selected_subdev(self.u, options.rx_subdev_spec) - self.cardtype = self.u.daughterboard_id(0) - - # Compute raw input rate - input_rate = self.u.adc_freq() / self.u.decim_rate() - - # BW==input_rate for complex data - self.bw = input_rate - - # - # Set baseband filter bandwidth if DBS_RX: - # - if self.cardtype == usrp_dbid.DBS_RX: - lbw = input_rate / 2 - if lbw < 1.0e6: - lbw = 1.0e6 - self.subdev.set_bw(lbw) - - # - # We use this as a crude volume control for the audio output - # - #self.volume = gr.multiply_const_ff(10**(-1)) - - - # - # Create location data for ephem package - # - self.locality = ephem.Observer() - self.locality.long = str(options.longitude) - self.locality.lat = str(options.latitude) - - # - # What is the post-detector LPF cutoff for the FFT? - # - PULSAR_MAX_FREQ=int(options.lowpass) - - # First low-pass filters down to input_rate/FIRST_FACTOR - # and decimates appropriately - FIRST_FACTOR=int(input_rate/(self.folder_input_rate/2)) - first_filter = gr.firdes.low_pass (1.0, - input_rate, - input_rate/FIRST_FACTOR, - input_rate/(FIRST_FACTOR*20), - gr.firdes.WIN_HAMMING) - - # Second filter runs at the output rate of the first filter, - # And low-pass filters down to PULSAR_MAX_FREQ*10 - # - second_input_rate = int(input_rate/(FIRST_FACTOR/2)) - second_filter = gr.firdes.band_pass(1.0, second_input_rate, - 0.10, - PULSAR_MAX_FREQ*10, - PULSAR_MAX_FREQ*1.5, - gr.firdes.WIN_HAMMING) - - # Third filter runs at PULSAR_MAX_FREQ*20 - # and filters down to PULSAR_MAX_FREQ - # - third_input_rate = PULSAR_MAX_FREQ*20 - third_filter = gr.firdes_band_pass(1.0, third_input_rate, - 0.10, PULSAR_MAX_FREQ, - PULSAR_MAX_FREQ/10.0, - gr.firdes.WIN_HAMMING) - - - # - # Create the appropriate FFT scope - # - self.scope = ra_fftsink.ra_fft_sink_f (panel, - fft_size=int(options.fft_size), sample_rate=PULSAR_MAX_FREQ*2, - title="Post-detector spectrum", - ofunc=self.pulsarfunc, xydfunc=self.xydfunc, fft_rate=200) - - # - # Tell scope we're looking from DC to PULSAR_MAX_FREQ - # - self.scope.set_baseband_freq (0.0) - - - # - # Setup stripchart for showing pulse profiles - # - hz = "%5.3fHz " % self.pulse_freq - per = "(%5.3f sec)" % (1.0/self.pulse_freq) - sr = "%d sps" % (int(self.pulse_freq*self.folding)) - times = " %d Pulse Intervals" % self.mult - self.chart = ra_stripchartsink.stripchart_sink_f (panel, - sample_rate=1, - stripsize=self.folding*FOLD_MULT, parallel=True, title="Pulse Profiles: "+hz+per+times, - xlabel="Seconds @ "+sr, ylabel="Level", autoscale=True, - divbase=self.divbase, scaling=1.0/(self.folding*self.pulse_freq)) - self.chart.set_ref_level(self.reflevel) - self.chart.set_y_per_div(self.division) - - # De-dispersion filter setup - # - # Do this here, just before creating the filter - # that will use the taps. - # - ntaps = self.compute_disp_ntaps(self.dm,self.bw,self.observing_freq) - - # Taps for the de-dispersion filter - self.disp_taps = Numeric.zeros(ntaps,Numeric.Complex64) - - # Compute the de-dispersion filter now - self.compute_dispfilter(self.dm,self.doppler, - self.bw,self.observing_freq) - - # - # Call constructors for receive chains - # - - # - # Now create the FFT filter using the computed taps - self.dispfilt = gr.fft_filter_ccc(1, self.disp_taps) - - # - # Audio sink - # - #print "input_rate ", second_input_rate, "audiodev ", self.audiodev - #self.audio = audio.sink(second_input_rate, self.audiodev) - - # - # The three post-detector filters - # Done this way to allow an audio path (up to 10Khz) - # ...and also because going from xMhz down to ~100Hz - # In a single filter doesn't seem to work. - # - self.first = gr.fir_filter_fff (FIRST_FACTOR/2, first_filter) - - p = second_input_rate / (PULSAR_MAX_FREQ*20) - self.second = gr.fir_filter_fff (int(p), second_filter) - self.third = gr.fir_filter_fff (10, third_filter) - - # Detector - self.detector = gr.complex_to_mag_squared() - - self.enable_comb_filter = False - # Epoch folder comb filter - if self.enable_comb_filter == True: - bogtaps = Numeric.zeros(512, Numeric.Float64) - self.folder_comb = gr.fft_filter_ccc(1,bogtaps) - - # Rational resampler - self.folder_rr = blks2.rational_resampler_fff(self.interp, self.decim) - - # Epoch folder bandpass - bogtaps = Numeric.zeros(1, Numeric.Float64) - self.folder_bandpass = gr.fir_filter_fff (1, bogtaps) - - # Epoch folder F2C/C2F - self.folder_f2c = gr.float_to_complex() - self.folder_c2f = gr.complex_to_float() - - # Epoch folder S2P - self.folder_s2p = gr.serial_to_parallel (gr.sizeof_float, - self.folding*FOLD_MULT) - - # Epoch folder IIR Filter (produces average pulse profiles) - self.folder_iir = gr.single_pole_iir_filter_ff(1.0/options.favg, - self.folding*FOLD_MULT) - - # - # Set all the epoch-folder goop up - # - self.set_folding_params() - - # - # Start connecting configured modules in the receive chain - # - - # Connect raw USRP to de-dispersion filter, detector - self.connect(self.u, self.dispfilt, self.detector) - - # Connect detector output to FIR LPF - # in two stages, followed by the FFT scope - self.connect(self.detector, self.first, - self.second, self.third, self.scope) - - # Connect audio output - #self.connect(self.first, self.volume) - #self.connect(self.volume, (self.audio, 0)) - #self.connect(self.volume, (self.audio, 1)) - - # Connect epoch folder - if self.enable_comb_filter == True: - self.connect (self.first, self.folder_bandpass, self.folder_rr, - self.folder_f2c, - self.folder_comb, self.folder_c2f, - self.folder_s2p, self.folder_iir, - self.chart) - - else: - self.connect (self.first, self.folder_bandpass, self.folder_rr, - self.folder_s2p, self.folder_iir, self.chart) - - # Connect baseband recording file (initially /dev/null) - self.connect(self.u, self.tofloat, self.tochar, self.recording) - - # Connect pulse recording file (initially /dev/null) - self.connect(self.first, self.toshort, self.pulse_recording) - - # - # Build the GUI elements - # - self._build_gui(vbox) - - # Make GUI agree with command-line - self.myform['average'].set_value(int(options.avg)) - self.myform['foldavg'].set_value(int(options.favg)) - - - # Make spectral averager agree with command line - if options.avg != 1.0: - self.scope.set_avg_alpha(float(1.0/options.avg)) - self.scope.set_average(True) - - - # set initial values - - if options.gain is None: - # if no gain was specified, use the mid-point in dB - g = self.subdev.gain_range() - options.gain = float(g[0]+g[1])/2 - - if options.freq is None: - # if no freq was specified, use the mid-point - r = self.subdev.freq_range() - options.freq = float(r[0]+r[1])/2 - - self.set_gain(options.gain) - #self.set_volume(-10.0) - - if not(self.set_freq(options.freq)): - self._set_status_msg("Failed to set initial frequency") - - self.myform['decim'].set_value(self.u.decim_rate()) - self.myform['fs@usb'].set_value(self.u.adc_freq() / self.u.decim_rate()) - self.myform['dbname'].set_value(self.subdev.name()) - self.myform['DM'].set_value(self.dm) - self.myform['Doppler'].set_value(self.doppler) - - # - # Start the timer that shows current LMST on the GUI - # - self.lmst_timer.Start(1000) - - - def _set_status_msg(self, msg): - self.frame.GetStatusBar().SetStatusText(msg, 0) - - def _build_gui(self, vbox): - - def _form_set_freq(kv): - return self.set_freq(kv['freq']) - - def _form_set_dm(kv): - return self.set_dm(kv['DM']) - - def _form_set_doppler(kv): - return self.set_doppler(kv['Doppler']) - - # Position the FFT or Waterfall - vbox.Add(self.scope.win, 5, wx.EXPAND) - vbox.Add(self.chart.win, 5, wx.EXPAND) - - # add control area at the bottom - self.myform = myform = form.form() - hbox = wx.BoxSizer(wx.HORIZONTAL) - hbox.Add((7,0), 0, wx.EXPAND) - vbox1 = wx.BoxSizer(wx.VERTICAL) - myform['freq'] = form.float_field( - parent=self.panel, sizer=vbox1, label="Center freq", weight=1, - callback=myform.check_input_and_call(_form_set_freq, self._set_status_msg)) - - vbox1.Add((3,0), 0, 0) - - # To show current Local Mean Sidereal Time - myform['lmst_high'] = form.static_text_field( - parent=self.panel, sizer=vbox1, label="Current LMST", weight=1) - vbox1.Add((3,0), 0, 0) - - # To show current spectral cursor data - myform['spec_data'] = form.static_text_field( - parent=self.panel, sizer=vbox1, label="Pulse Freq", weight=1) - vbox1.Add((3,0), 0, 0) - - # To show best pulses found in FFT output - myform['best_pulse'] = form.static_text_field( - parent=self.panel, sizer=vbox1, label="Best freq", weight=1) - vbox1.Add((3,0), 0, 0) - - vboxBogus = wx.BoxSizer(wx.VERTICAL) - vboxBogus.Add ((2,0), 0, wx.EXPAND) - vbox2 = wx.BoxSizer(wx.VERTICAL) - g = self.subdev.gain_range() - myform['gain'] = form.slider_field(parent=self.panel, sizer=vbox2, label="RF Gain", - weight=1, - min=int(g[0]), max=int(g[1]), - callback=self.set_gain) - - vbox2.Add((6,0), 0, 0) - myform['average'] = form.slider_field(parent=self.panel, sizer=vbox2, - label="Spectral Averaging", weight=1, min=1, max=200, callback=self.set_averaging) - vbox2.Add((6,0), 0, 0) - myform['foldavg'] = form.slider_field(parent=self.panel, sizer=vbox2, - label="Folder Averaging", weight=1, min=1, max=20, callback=self.set_folder_averaging) - vbox2.Add((6,0), 0, 0) - #myform['volume'] = form.quantized_slider_field(parent=self.panel, sizer=vbox2, - #label="Audio Volume", weight=1, range=(-20, 0, 0.5), callback=self.set_volume) - #vbox2.Add((6,0), 0, 0) - myform['DM'] = form.float_field( - parent=self.panel, sizer=vbox2, label="DM", weight=1, - callback=myform.check_input_and_call(_form_set_dm)) - vbox2.Add((6,0), 0, 0) - myform['Doppler'] = form.float_field( - parent=self.panel, sizer=vbox2, label="Doppler", weight=1, - callback=myform.check_input_and_call(_form_set_doppler)) - vbox2.Add((6,0), 0, 0) - - - # Baseband recording control - buttonbox = wx.BoxSizer(wx.HORIZONTAL) - self.record_control = form.button_with_callback(self.panel, - label="Recording baseband: Off ", - callback=self.toggle_recording) - self.record_pulse_control = form.button_with_callback(self.panel, - label="Recording pulses: Off ", - callback=self.toggle_pulse_recording) - - buttonbox.Add(self.record_control, 0, wx.CENTER) - buttonbox.Add(self.record_pulse_control, 0, wx.CENTER) - vbox.Add(buttonbox, 0, wx.CENTER) - hbox.Add(vbox1, 0, 0) - hbox.Add(vboxBogus, 0, 0) - hbox.Add(vbox2, wx.ALIGN_RIGHT, 0) - vbox.Add(hbox, 0, wx.EXPAND) - - self._build_subpanel(vbox) - - self.lmst_timer = wx.PyTimer(self.lmst_timeout) - self.lmst_timeout() - - - def _build_subpanel(self, vbox_arg): - # build a secondary information panel (sometimes hidden) - - # FIXME figure out how to have this be a subpanel that is always - # created, but has its visibility controlled by foo.Show(True/False) - - if not(self.show_debug_info): - return - - panel = self.panel - vbox = vbox_arg - myform = self.myform - - #panel = wx.Panel(self.panel, -1) - #vbox = wx.BoxSizer(wx.VERTICAL) - - hbox = wx.BoxSizer(wx.HORIZONTAL) - hbox.Add((5,0), 0) - myform['decim'] = form.static_float_field( - parent=panel, sizer=hbox, label="Decim") - - hbox.Add((5,0), 1) - myform['fs@usb'] = form.static_float_field( - parent=panel, sizer=hbox, label="Fs@USB") - - hbox.Add((5,0), 1) - myform['dbname'] = form.static_text_field( - parent=panel, sizer=hbox) - - hbox.Add((5,0), 1) - myform['baseband'] = form.static_float_field( - parent=panel, sizer=hbox, label="Analog BB") - - hbox.Add((5,0), 1) - myform['ddc'] = form.static_float_field( - parent=panel, sizer=hbox, label="DDC") - - hbox.Add((5,0), 0) - vbox.Add(hbox, 0, wx.EXPAND) - - - - def set_freq(self, target_freq): - """ - Set the center frequency we're interested in. - - @param target_freq: frequency in Hz - @rypte: bool - - Tuning is a two step process. First we ask the front-end to - tune as close to the desired frequency as it can. Then we use - the result of that operation and our target_frequency to - determine the value for the digital down converter. - """ - r = usrp.tune(self.u, 0, self.subdev, target_freq) - - if r: - self.myform['freq'].set_value(target_freq) # update displayed value - self.myform['baseband'].set_value(r.baseband_freq) - self.myform['ddc'].set_value(r.dxc_freq) - # Adjust self.frequency, and self.observing_freq - # We pick up the difference between the current self.frequency - # and the just-programmed one, and use this to adjust - # self.observing_freq. We have to do it this way to - # make the dedispersion filtering work out properly. - delta = target_freq - self.frequency - self.frequency = target_freq - self.observing_freq += delta - - # Now that we're adjusted, compute a new dispfilter, and - # set the taps for the FFT filter. - ntaps = self.compute_disp_ntaps(self.dm, self.bw, self.observing_freq) - self.disp_taps = Numeric.zeros(ntaps, Numeric.Complex64) - self.compute_dispfilter(self.dm,self.doppler,self.bw, - self.observing_freq) - self.dispfilt.set_taps(self.disp_taps) - - return True - - return False - - # Callback for gain-setting slider - def set_gain(self, gain): - self.myform['gain'].set_value(gain) # update displayed value - self.subdev.set_gain(gain) - - - #def set_volume(self, vol): - #self.myform['volume'].set_value(vol) - #self.volume.set_k((10**(vol/10))/8192) - - # Callback for spectral-averaging slider - def set_averaging(self, avval): - self.myform['average'].set_value(avval) - self.scope.set_avg_alpha(1.0/(avval)) - self.scope.set_average(True) - - def set_folder_averaging(self, avval): - self.myform['foldavg'].set_value(avval) - self.folder_iir.set_taps(1.0/avval) - - # Timer callback to update LMST display - def lmst_timeout(self): - self.locality.date = ephem.now() - sidtime = self.locality.sidereal_time() - self.myform['lmst_high'].set_value(str(ephem.hours(sidtime))) - - # - # Turn recording on/off - # Called-back by "Recording" button - # - def toggle_recording(self): - # Pick up current LMST - self.locality.date = ephem.now() - sidtime = self.locality.sidereal_time() - - # Pick up localtime, for generating filenames - foo = time.localtime() - - # Generate filenames for both data and header file - filename = "%04d%02d%02d%02d%02d.pdat" % (foo.tm_year, foo.tm_mon, - foo.tm_mday, foo.tm_hour, foo.tm_min) - hdrfilename = "%04d%02d%02d%02d%02d.phdr" % (foo.tm_year, foo.tm_mon, - foo.tm_mday, foo.tm_hour, foo.tm_min) - - # Current recording? Flip state - if (self.recording_state == True): - self.recording_state = False - self.record_control.SetLabel("Recording baseband: Off ") - self.recording.close() - # Not recording? - else: - self.recording_state = True - self.record_control.SetLabel("Recording baseband to: "+filename) - - # Cause gr_file_sink object to accept new filename - # note use of self.prefix--filename prefix from - # command line (defaults to ./) - # - self.recording.open (self.prefix+filename) - - # - # We open the header file as a regular file, write header data, - # then close - hdrf = open(self.prefix+hdrfilename, "w") - hdrf.write("receiver center frequency: "+str(self.frequency)+"\n") - hdrf.write("observing frequency: "+str(self.observing_freq)+"\n") - hdrf.write("DM: "+str(self.dm)+"\n") - hdrf.write("doppler: "+str(self.doppler)+"\n") - - hdrf.write("sidereal: "+str(ephem.hours(sidtime))+"\n") - hdrf.write("bandwidth: "+str(self.u.adc_freq() / self.u.decim_rate())+"\n") - hdrf.write("sample type: complex_char\n") - hdrf.write("sample size: "+str(gr.sizeof_char*2)+"\n") - hdrf.close() - # - # Turn recording on/off - # Called-back by "Recording" button - # - def toggle_pulse_recording(self): - # Pick up current LMST - self.locality.date = ephem.now() - sidtime = self.locality.sidereal_time() - - # Pick up localtime, for generating filenames - foo = time.localtime() - - # Generate filenames for both data and header file - filename = "%04d%02d%02d%02d%02d.padat" % (foo.tm_year, foo.tm_mon, - foo.tm_mday, foo.tm_hour, foo.tm_min) - hdrfilename = "%04d%02d%02d%02d%02d.pahdr" % (foo.tm_year, foo.tm_mon, - foo.tm_mday, foo.tm_hour, foo.tm_min) - - # Current recording? Flip state - if (self.pulse_recording_state == True): - self.pulse_recording_state = False - self.record_pulse_control.SetLabel("Recording pulses: Off ") - self.pulse_recording.close() - # Not recording? - else: - self.pulse_recording_state = True - self.record_pulse_control.SetLabel("Recording pulses to: "+filename) - - # Cause gr_file_sink object to accept new filename - # note use of self.prefix--filename prefix from - # command line (defaults to ./) - # - self.pulse_recording.open (self.prefix+filename) - - # - # We open the header file as a regular file, write header data, - # then close - hdrf = open(self.prefix+hdrfilename, "w") - hdrf.write("receiver center frequency: "+str(self.frequency)+"\n") - hdrf.write("observing frequency: "+str(self.observing_freq)+"\n") - hdrf.write("DM: "+str(self.dm)+"\n") - hdrf.write("doppler: "+str(self.doppler)+"\n") - hdrf.write("pulse rate: "+str(self.pulse_freq)+"\n") - hdrf.write("pulse sps: "+str(self.pulse_freq*self.folding)+"\n") - hdrf.write("file sps: "+str(self.folder_input_rate)+"\n") - - hdrf.write("sidereal: "+str(ephem.hours(sidtime))+"\n") - hdrf.write("bandwidth: "+str(self.u.adc_freq() / self.u.decim_rate())+"\n") - hdrf.write("sample type: short\n") - hdrf.write("sample size: 1\n") - hdrf.close() - - # We get called at startup, and whenever the GUI "Set Folding params" - # button is pressed - # - def set_folding_params(self): - if (self.pulse_freq <= 0): - return - - # Compute required sample rate - self.sample_rate = int(self.pulse_freq*self.folding) - - # And the implied decimation rate - required_decimation = int(self.folder_input_rate / self.sample_rate) - - # We also compute a new FFT comb filter, based on the expected - # spectral profile of our pulse parameters - # - # FFT-based comb filter - # - N_COMB_TAPS=int(self.sample_rate*4) - if N_COMB_TAPS > 2000: - N_COMB_TAPS = 2000 - self.folder_comb_taps = Numeric.zeros(N_COMB_TAPS,Numeric.Complex64) - fincr = (self.sample_rate)/float(N_COMB_TAPS) - for i in range(0,len(self.folder_comb_taps)): - self.folder_comb_taps[i] = complex(0.0, 0.0) - - freq = 0.0 - harmonics = [1.0,2.0,3.0,4.0,5.0,6.0,7.0] - for i in range(0,len(self.folder_comb_taps)/2): - for j in range(0,len(harmonics)): - if abs(freq - harmonics[j]*self.pulse_freq) <= fincr: - self.folder_comb_taps[i] = complex(4.0, 0.0) - if harmonics[j] == 1.0: - self.folder_comb_taps[i] = complex(8.0, 0.0) - freq += fincr - - if self.enable_comb_filter == True: - # Set the just-computed FFT comb filter taps - self.folder_comb.set_taps(self.folder_comb_taps) - - # And compute a new decimated bandpass filter, to go in front - # of the comb. Primary function is to decimate and filter down - # to an exact-ish multiple of the target pulse rate - # - self.folding_taps = gr.firdes_band_pass (1.0, self.folder_input_rate, - 0.10, self.sample_rate/2, 10, - gr.firdes.WIN_HAMMING) - - # Set the computed taps for the bandpass/decimate filter - self.folder_bandpass.set_taps (self.folding_taps) - # - # Record a spectral "hit" of a possible pulsar spectral profile - # - def record_hit(self,hits, hcavg, hcmax): - # Pick up current LMST - self.locality.date = ephem.now() - sidtime = self.locality.sidereal_time() - - # Pick up localtime, for generating filenames - foo = time.localtime() - - # Generate filenames for both data and header file - hitfilename = "%04d%02d%02d%02d.phit" % (foo.tm_year, foo.tm_mon, - foo.tm_mday, foo.tm_hour) - - hitf = open(self.prefix+hitfilename, "a") - hitf.write("receiver center frequency: "+str(self.frequency)+"\n") - hitf.write("observing frequency: "+str(self.observing_freq)+"\n") - hitf.write("DM: "+str(self.dm)+"\n") - hitf.write("doppler: "+str(self.doppler)+"\n") - - hitf.write("sidereal: "+str(ephem.hours(sidtime))+"\n") - hitf.write("bandwidth: "+str(self.u.adc_freq() / self.u.decim_rate())+"\n") - hitf.write("spectral peaks: "+str(hits)+"\n") - hitf.write("HCM: "+str(hcavg)+" "+str(hcmax)+"\n") - hitf.close() - - # This is a callback used by ra_fftsink.py (passed on creation of - # ra_fftsink) - # Whenever the user moves the cursor within the FFT display, this - # shows the coordinate data - # - def xydfunc(self,xyv): - s = "%.6fHz\n%.3fdB" % (xyv[0], xyv[1]) - if self.lowpass >= 500: - s = "%.6fHz\n%.3fdB" % (xyv[0]*1000, xyv[1]) - - self.myform['spec_data'].set_value(s) - - # This is another callback used by ra_fftsink.py (passed on creation - # of ra_fftsink). We pass this as our "calibrator" function, but - # we create interesting side-effects in the GUI. - # - # This function finds peaks in the FFT output data, and reports - # on them through the "Best" text object in the GUI - # It also computes the Harmonic Compliance Measure (HCM), and displays - # that also. - # - def pulsarfunc(self,d,l): - x = range(0,l) - incr = float(self.lowpass)/float(l) - incr = incr * 2.0 - bestdb = -50.0 - bestfreq = 0.0 - avg = 0 - dcnt = 0 - # - # First, we need to find the average signal level - # - for i in x: - if (i * incr) > self.lowest_freq and (i*incr) < (self.lowpass-2): - avg += d[i] - dcnt += 1 - # Set average signal level - avg /= dcnt - s2=" " - findcnt = 0 - # - # Then we find candidates that are greater than the user-supplied - # threshold. - # - # We try to cluster "hits" whose whole-number frequency is the - # same, and compute an average "hit" frequency. - # - lastint = 0 - hits=[] - intcnt = 0 - freqavg = 0 - for i in x: - freq = i*incr - # If frequency within bounds, and the (dB-avg) value is above our - # threshold - if freq > self.lowest_freq and freq < self.lowpass-2 and (d[i] - avg) > self.threshold: - # If we're finding a new whole-number frequency - if lastint != int(freq): - # Record "center" of this hit, if this is a new hit - if lastint != 0: - s2 += "%5.3fHz " % (freqavg/intcnt) - hits.append(freqavg/intcnt) - findcnt += 1 - lastint = int(freq) - intcnt = 1 - freqavg = freq - else: - intcnt += 1 - freqavg += freq - if (findcnt >= 14): - break - - if intcnt > 1: - s2 += "%5.3fHz " % (freqavg/intcnt) - hits.append(freqavg/intcnt) - - # - # Compute the HCM, by dividing each of the "hits" by each of the - # other hits, and comparing the difference between a "perfect" - # harmonic, and the observed frequency ratio. - # - measure = 0 - max_measure=0 - mcnt = 0 - avg_dist = 0 - acnt = 0 - for i in range(1,len(hits)): - meas = hits[i]/hits[0] - int(hits[i]/hits[0]) - if abs((hits[i]-hits[i-1])-hits[0]) < 0.1: - avg_dist += hits[i]-hits[i-1] - acnt += 1 - if meas > 0.98 and meas < 1.0: - meas = 1.0 - meas - meas *= hits[0] - if meas >= max_measure: - max_measure = meas - measure += meas - mcnt += 1 - if mcnt > 0: - measure /= mcnt - if acnt > 0: - avg_dist /= acnt - if len(hits) > 1: - measure /= mcnt - s3="\nHCM: Avg %5.3fHz(%d) Max %5.3fHz Dist %5.3fHz(%d)" % (measure,mcnt,max_measure, avg_dist, acnt) - if max_measure < 0.5 and len(hits) >= 2: - self.record_hit(hits, measure, max_measure) - self.avg_dist = avg_dist - else: - s3="\nHCM: --" - s4="\nAvg dB: %4.2f" % avg - self.myform['best_pulse'].set_value("("+s2+")"+s3+s4) - - # Since we are nominally a calibrator function for ra_fftsink, we - # simply return what they sent us, untouched. A "real" calibrator - # function could monkey with the data before returning it to the - # FFT display function. - return(d) - - # - # Callback for the "DM" gui object - # - # We call compute_dispfilter() as appropriate to compute a new filter, - # and then set that new filter into self.dispfilt. - # - def set_dm(self,dm): - self.dm = dm - - ntaps = self.compute_disp_ntaps (self.dm, self.bw, self.observing_freq) - self.disp_taps = Numeric.zeros(ntaps, Numeric.Complex64) - self.compute_dispfilter(self.dm,self.doppler,self.bw,self.observing_freq) - self.dispfilt.set_taps(self.disp_taps) - self.myform['DM'].set_value(dm) - return(dm) - - # - # Callback for the "Doppler" gui object - # - # We call compute_dispfilter() as appropriate to compute a new filter, - # and then set that new filter into self.dispfilt. - # - def set_doppler(self,doppler): - self.doppler = doppler - - ntaps = self.compute_disp_ntaps (self.dm, self.bw, self.observing_freq) - self.disp_taps = Numeric.zeros(ntaps, Numeric.Complex64) - self.compute_dispfilter(self.dm,self.doppler,self.bw,self.observing_freq) - self.dispfilt.set_taps(self.disp_taps) - self.myform['Doppler'].set_value(doppler) - return(doppler) - - # - # Compute a de-dispersion filter - # From Hankins, et al, 1975 - # - # This code translated from dedisp_filter.c from Swinburne - # pulsar software repository - # - def compute_dispfilter(self,dm,doppler,bw,centerfreq): - npts = len(self.disp_taps) - tmp = Numeric.zeros(npts, Numeric.Complex64) - M_PI = 3.14159265358 - DM = dm/2.41e-10 - - # - # Because astronomers are a crazy bunch, the "standard" calcultion - # is in Mhz, rather than Hz - # - centerfreq = centerfreq / 1.0e6 - bw = bw / 1.0e6 - - isign = int(bw / abs (bw)) - - # Center frequency may be doppler shifted - cfreq = centerfreq / doppler - - # As well as the bandwidth.. - bandwidth = bw / doppler - - # Bandwidth divided among bins - binwidth = bandwidth / npts - - # Delay is an "extra" parameter, in usecs, and largely - # untested in the Swinburne code. - delay = 0.0 - - # This determines the coefficient of the frequency response curve - # Linear in DM, but quadratic in center frequency - coeff = isign * 2.0*M_PI * DM / (cfreq*cfreq) - - # DC to nyquist/2 - n = 0 - for i in range(0,int(npts/2)): - freq = (n + 0.5) * binwidth - phi = coeff*freq*freq/(cfreq+freq) + (2.0*M_PI*freq*delay) - tmp[i] = complex(math.cos(phi), math.sin(phi)) - n += 1 - - # -nyquist/2 to DC - n = int(npts/2) - n *= -1 - for i in range(int(npts/2),npts): - freq = (n + 0.5) * binwidth - phi = coeff*freq*freq/(cfreq+freq) + (2.0*M_PI*freq*delay) - tmp[i] = complex(math.cos(phi), math.sin(phi)) - n += 1 - - self.disp_taps = numpy.fft.ifft(tmp) - return(self.disp_taps) - - # - # Compute minimum number of taps required in de-dispersion FFT filter - # - def compute_disp_ntaps(self,dm,bw,freq): - # - # Dt calculations are in Mhz, rather than Hz - # crazy astronomers.... - mbw = bw/1.0e6 - mfreq = freq/1.0e6 - - f_lower = mfreq-(mbw/2) - f_upper = mfreq+(mbw/2) - - # Compute smear time - Dt = dm/2.41e-4 * (1.0/(f_lower*f_lower)-1.0/(f_upper*f_upper)) - - # ntaps is now bandwidth*smeartime - # Should be bandwidth*smeartime*2, but the Gnu Radio FFT filter - # already expands it by a factor of 2 - ntaps = bw*Dt - if ntaps < 64: - ntaps = 64 - return(int(ntaps)) - -def main (): - app = stdgui2.stdapp(app_flow_graph, "RADIO ASTRONOMY PULSAR RECEIVER: $Revision$", nstatus=1) - app.MainLoop() - -if __name__ == '__main__': - main () diff --git a/gr-radio-astronomy/src/python/usrp_ra_receiver.help b/gr-radio-astronomy/src/python/usrp_ra_receiver.help deleted file mode 100644 index 45a21e297..000000000 --- a/gr-radio-astronomy/src/python/usrp_ra_receiver.help +++ /dev/null @@ -1,90 +0,0 @@ -This program is used to take spectra and total power measurements. - It records spectral and total-power data to external datalogging - files. - -The program takes the following options: - - --rx-subdev-spec which USRP Rx side? A or B - - --decim USRP decimation rate: 8, 16, 32, and 64 are good - (8Mhz, 4Mhz, 2Mhz, and 1Mhz bandwidth) - - --freq USRP daughtercard frequency - - --observing Actual observing frequency (default is to use the - setting for --freq) - - --avg Averaging setting for spectral display--higher numbers - equal more averaging. 25 to 40 is typical. - - --integ Total power integration time: seconds - - --gain USRP daughtercard gain control - - --reflevel Reference level on pulse profile display - - --longitude Observer longitude: West is negative - - --latitude Observer latitude: South is negative - - --fft_size Size of FFT for post-detector spectrum: default is 1024 - - --prefix Filename prefix to use for data logging files - Default is ./ - - --divbase The base of the Y/Div menu in pulsar display - - --division The initial Y/Div in pulsar display - - --ylabel Y axis label - - --cfunc The function name prefix for the spectral and - calibrator functions - - --waterfall Use waterfall, rather than regular spectral display - NOT TESTED IN THIS APPLICATION - - --stripsize Size of the total-power stripchart, in samples - -There are two windows--a spectral window, and the total-power window. - Moving the cursor around in the spectral window shows you the - corresponding frequency and doppler shift. Left clicking in this - window sets an interference marker, which sets a "zero" in the - interference filter. Use the "clear interference" button to clear this. - -The total power window is updated at a fixed 2Hz rate, and grows from - the left of the display. - -If latitude and longitude are set correctly, and system time is correct, - then the current LMST is displayed, updated once per second. - -Averaging parameters, gain, and frequency can all be set from the GUI using - the appropriate controls. You can also enter the current declination, which - will appear in the datalogging files. This is useful both for mapping, - and housekeeping purposes, particularly when you haven't looked at a datafile - for quite some time. - -There are two datalog files produced by this program: - - YYYYMMDDHH.tpdat Total power data - - The date/time portion of the filename is referred to local time, - rather than UTC or sidereal. - - First field is sidereal time when sample was taken - Second field is total power datum - Third field is declination in decimal degrees - - Samples are written once per second - - YYYYMMDDHH.sdat Spectral data - - The date/time portion of the filename is referred to local time, - rather than UTC or sidereal. - - First field is sidereal time when spectrum was taken - Second field is declination in decimal degrees - Third field is complex spectral data--in the same order that FFTW3 library - places bins: DC to bandwidth/2, followed by -bandwidth/2 to DC. - - Spectral snapshots are written once every 5 seconds diff --git a/gr-radio-astronomy/src/python/usrp_ra_receiver.py b/gr-radio-astronomy/src/python/usrp_ra_receiver.py deleted file mode 100755 index c37355d28..000000000 --- a/gr-radio-astronomy/src/python/usrp_ra_receiver.py +++ /dev/null @@ -1,1384 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2004,2005,2007 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. -# - -from gnuradio import gr, gru -from gnuradio import usrp -from usrpm import usrp_dbid -from gnuradio import eng_notation -from gnuradio.eng_option import eng_option -from gnuradio.wxgui import stdgui2, ra_fftsink, ra_stripchartsink, ra_waterfallsink, form, slider -from optparse import OptionParser -import wx -import sys -import Numeric -import time -import numpy.fft -import ephem - -class app_flow_graph(stdgui2.std_top_block): - def __init__(self, frame, panel, vbox, argv): - stdgui2.std_top_block.__init__(self, frame, panel, vbox, argv) - - self.frame = frame - self.panel = panel - - parser = OptionParser(option_class=eng_option) - parser.add_option("-R", "--rx-subdev-spec", type="subdev", default=(0, 0), - help="select USRP Rx side A or B (default=A)") - parser.add_option("-d", "--decim", type="int", default=16, - help="set fgpa decimation rate to DECIM [default=%default]") - parser.add_option("-f", "--freq", type="eng_float", default=None, - help="set frequency to FREQ", metavar="FREQ") - parser.add_option("-a", "--avg", type="eng_float", default=1.0, - help="set spectral averaging alpha") - parser.add_option("-i", "--integ", type="eng_float", default=1.0, - help="set integration time") - parser.add_option("-g", "--gain", type="eng_float", default=None, - help="set gain in dB (default is midpoint)") - parser.add_option("-l", "--reflevel", type="eng_float", default=30.0, - help="Set Total power reference level") - parser.add_option("-y", "--division", type="eng_float", default=0.5, - help="Set Total power Y division size") - parser.add_option("-e", "--longitude", type="eng_float", default=-76.02,help="Set Observer Longitude") - parser.add_option("-c", "--latitude", type="eng_float", default=44.85,help="Set Observer Latitude") - parser.add_option("-o", "--observing", type="eng_float", default=0.0, - help="Set observing frequency") - parser.add_option("-x", "--ylabel", default="dB", help="Y axis label") - parser.add_option("-z", "--divbase", type="eng_float", default=0.025, help="Y Division increment base") - parser.add_option("-v", "--stripsize", type="eng_float", default=2400, help="Size of stripchart, in 2Hz samples") - parser.add_option("-F", "--fft_size", type="eng_float", default=1024, help="Size of FFT") - parser.add_option("-N", "--decln", type="eng_float", default=999.99, help="Observing declination") - parser.add_option("-X", "--prefix", default="./") - parser.add_option("-M", "--fft_rate", type="eng_float", default=8.0, help="FFT Rate") - parser.add_option("-A", "--calib_coeff", type="eng_float", default=1.0, help="Calibration coefficient") - parser.add_option("-B", "--calib_offset", type="eng_float", default=0.0, help="Calibration coefficient") - parser.add_option("-W", "--waterfall", action="store_true", default=False, help="Use Waterfall FFT display") - parser.add_option("-S", "--setimode", action="store_true", default=False, help="Enable SETI processing of spectral data") - parser.add_option("-K", "--setik", type="eng_float", default=1.5, help="K value for SETI analysis") - parser.add_option("-T", "--setibandwidth", type="eng_float", default=12500, help="Instantaneous SETI observing bandwidth--must be divisor of 250Khz") - parser.add_option("-Q", "--seti_range", type="eng_float", default=1.0e6, help="Total scan width, in Hz for SETI scans") - parser.add_option("-Z", "--dual_mode", action="store_true", - default=False, help="Dual-polarization mode") - parser.add_option("-I", "--interferometer", action="store_true", default=False, help="Interferometer mode") - parser.add_option("-D", "--switch_mode", action="store_true", default=False, help="Dicke Switching mode") - parser.add_option("-P", "--reference_divisor", type="eng_float", default=1.0, help="Reference Divisor") - parser.add_option("-U", "--ref_fifo", default=None) - parser.add_option("-k", "--notch_taps", type="int", default=64, help="Number of notch taps") - parser.add_option("-n", "--notches", action="store_true", - default=False, help="Notch frequencies after all other args") - parser.add_option("-Y", "--interface", default=None) - parser.add_option("-H", "--mac_addr", default=None) - - # Added this documentation - - (options, args) = parser.parse_args() - - self.setimode = options.setimode - self.dual_mode = options.dual_mode - self.interferometer = options.interferometer - self.normal_mode = False - self.switch_mode = options.switch_mode - self.switch_state = 0 - self.reference_divisor = options.reference_divisor - self.ref_fifo = options.ref_fifo - self.usrp2 = False - self.decim = options.decim - self.rx_subdev_spec = options.rx_subdev_spec - - if (options.interface != None and options.mac_addr != None): - self.mac_addr = options.mac_addr - self.interface = options.interface - self.usrp2 = True - - self.NOTCH_TAPS = options.notch_taps - self.notches = Numeric.zeros(self.NOTCH_TAPS,Numeric.Float64) - # Get notch locations - j = 0 - for i in args: - self.notches[j] = float(i) - j = j + 1 - - self.use_notches = options.notches - - if (self.ref_fifo != None): - self.ref_fifo_file = open (self.ref_fifo, "r") - - modecount = 0 - for modes in (self.dual_mode, self.interferometer): - if (modes == True): - modecount = modecount + 1 - - if (modecount > 1): - print "must select only 1 of --dual_mode, or --interferometer" - sys.exit(1) - - self.chartneeded = True - - if (self.setimode == True): - self.chartneeded = False - - if (self.setimode == True and self.interferometer == True): - print "can't pick both --setimode and --interferometer" - sys.exit(1) - - if (self.setimode == True and self.switch_mode == True): - print "can't pick both --setimode and --switch_mode" - sys.exit(1) - - if (self.interferometer == True and self.switch_mode == True): - print "can't pick both --interferometer and --switch_mode" - sys.exit(1) - - if (modecount == 0): - self.normal_mode = True - - self.show_debug_info = True - - # Pick up waterfall option - self.waterfall = options.waterfall - - # SETI mode stuff - self.setimode = options.setimode - self.seticounter = 0 - self.setik = options.setik - self.seti_fft_bandwidth = int(options.setibandwidth) - - # Calculate binwidth - binwidth = self.seti_fft_bandwidth / options.fft_size - - # Use binwidth, and knowledge of likely chirp rates to set reasonable - # values for SETI analysis code. We assume that SETI signals will - # chirp at somewhere between 0.10Hz/sec and 0.25Hz/sec. - # - # upper_limit is the "worst case"--that is, the case for which we have - # to wait the longest to actually see any drift, due to the quantizing - # on FFT bins. - upper_limit = binwidth / 0.10 - self.setitimer = int(upper_limit * 2.00) - self.scanning = True - - # Calculate the CHIRP values based on Hz/sec - self.CHIRP_LOWER = 0.10 * self.setitimer - self.CHIRP_UPPER = 0.25 * self.setitimer - - # Reset hit counters to 0 - self.hitcounter = 0 - self.s1hitcounter = 0 - self.s2hitcounter = 0 - self.avgdelta = 0 - # We scan through 2Mhz of bandwidth around the chosen center freq - self.seti_freq_range = options.seti_range - # Calculate lower edge - self.setifreq_lower = options.freq - (self.seti_freq_range/2) - self.setifreq_current = options.freq - # Calculate upper edge - self.setifreq_upper = options.freq + (self.seti_freq_range/2) - - # Maximum "hits" in a line - self.nhits = 20 - - # Number of lines for analysis - self.nhitlines = 4 - - # We change center frequencies based on nhitlines and setitimer - self.setifreq_timer = self.setitimer * (self.nhitlines * 5) - - # Create actual timer - self.seti_then = time.time() - - # The hits recording array - self.hits_array = Numeric.zeros((self.nhits,self.nhitlines), Numeric.Float64) - self.hit_intensities = Numeric.zeros((self.nhits,self.nhitlines), Numeric.Float64) - # Calibration coefficient and offset - self.calib_coeff = options.calib_coeff - self.calib_offset = options.calib_offset - if self.calib_offset < -750: - self.calib_offset = -750 - if self.calib_offset > 750: - self.calib_offset = 750 - - if self.calib_coeff < 1: - self.calib_coeff = 1 - if self.calib_coeff > 100: - self.calib_coeff = 100 - - self.integ = options.integ - self.avg_alpha = options.avg - self.gain = options.gain - self.decln = options.decln - - # Set initial values for datalogging timed-output - self.continuum_then = time.time() - self.spectral_then = time.time() - - - # build the graph - - self.subdev = [(0, 0), (0,0)] - - # - # If SETI mode, we always run at maximum USRP decimation - # - if (self.setimode): - options.decim = 256 - - if (self.dual_mode == True and self.decim <= 4): - print "Cannot use decim <= 4 with dual_mode" - sys.exit(1) - - self.setup_usrp() - - # Set initial declination - self.decln = options.decln - - input_rate = self.u.adc_freq() / self.u.decim_rate() - self.bw = input_rate - # - # Set prefix for data files - # - self.prefix = options.prefix - - # - # The lower this number, the fewer sample frames are dropped - # in computing the FFT. A sampled approach is taken to - # computing the FFT of the incoming data, which reduces - # sensitivity. Increasing sensitivity inreases CPU loading. - # - self.fft_rate = options.fft_rate - - self.fft_size = int(options.fft_size) - - # This buffer is used to remember the most-recent FFT display - # values. Used later by self.write_spectral_data() to write - # spectral data to datalogging files, and by the SETI analysis - # function. - # - self.fft_outbuf = Numeric.zeros(self.fft_size, Numeric.Float64) - - # - # If SETI mode, only look at seti_fft_bandwidth - # at a time. - # - if (self.setimode): - self.fft_input_rate = self.seti_fft_bandwidth - - # - # Build a decimating bandpass filter - # - self.fft_input_taps = gr.firdes.complex_band_pass (1.0, - input_rate, - -(int(self.fft_input_rate/2)), int(self.fft_input_rate/2), 200, - gr.firdes.WIN_HAMMING, 0) - - # - # Compute required decimation factor - # - decimation = int(input_rate/self.fft_input_rate) - self.fft_bandpass = gr.fir_filter_ccc (decimation, - self.fft_input_taps) - else: - self.fft_input_rate = input_rate - - # Set up FFT display - if self.waterfall == False: - self.scope = ra_fftsink.ra_fft_sink_c (panel, - fft_size=int(self.fft_size), sample_rate=self.fft_input_rate, - fft_rate=int(self.fft_rate), title="Spectral", - ofunc=self.fft_outfunc, xydfunc=self.xydfunc) - else: - self.scope = ra_waterfallsink.waterfall_sink_c (panel, - fft_size=int(self.fft_size), sample_rate=self.fft_input_rate, - fft_rate=int(self.fft_rate), title="Spectral", ofunc=self.fft_outfunc, size=(1100, 600), xydfunc=self.xydfunc, ref_level=0, span=10) - - # Set up ephemeris data - self.locality = ephem.Observer() - self.locality.long = str(options.longitude) - self.locality.lat = str(options.latitude) - - # We make notes about Sunset/Sunrise in Continuum log files - self.sun = ephem.Sun() - self.sunstate = "??" - - # Set up stripchart display - tit = "Continuum" - if (self.dual_mode != False): - tit = "H+V Continuum" - if (self.interferometer != False): - tit = "East x West Correlation" - self.stripsize = int(options.stripsize) - if self.chartneeded == True: - self.chart = ra_stripchartsink.stripchart_sink_f (panel, - stripsize=self.stripsize, - title=tit, - xlabel="LMST Offset (Seconds)", - scaling=1.0, ylabel=options.ylabel, - divbase=options.divbase) - - # Set center frequency - self.centerfreq = options.freq - - # Set observing frequency (might be different from actual programmed - # RF frequency) - if options.observing == 0.0: - self.observing = options.freq - else: - self.observing = options.observing - - # Remember our input bandwidth - self.bw = input_rate - - # - # - # The strip chart is fed at a constant 1Hz rate - # - - # - # Call constructors for receive chains - # - - if (self.dual_mode == True): - self.setup_dual (self.setimode) - - if (self.interferometer == True): - self.setup_interferometer(self.setimode) - - if (self.normal_mode == True): - self.setup_normal(self.setimode) - - if (self.setimode == True): - self.setup_seti() - - self._build_gui(vbox) - - # Make GUI agree with command-line - self.integ = options.integ - if self.setimode == False: - self.myform['integration'].set_value(int(options.integ)) - self.myform['offset'].set_value(self.calib_offset) - self.myform['dcgain'].set_value(self.calib_coeff) - self.myform['average'].set_value(int(options.avg)) - - - if self.setimode == False: - # Make integrator agree with command line - self.set_integration(int(options.integ)) - - self.avg_alpha = options.avg - - # Make spectral averager agree with command line - if options.avg != 1.0: - self.scope.set_avg_alpha(float(1.0/options.avg)) - self.scope.set_average(True) - - if self.setimode == False: - # Set division size - self.chart.set_y_per_div(options.division) - # Set reference(MAX) level - self.chart.set_ref_level(options.reflevel) - - # set initial values - - if options.gain is None: - # if no gain was specified, use the mid-point in dB - g = self.subdev[0].gain_range() - options.gain = float(g[0]+g[1])/2 - - if options.freq is None: - # if no freq was specified, use the mid-point - r = self.subdev[0].freq_range() - options.freq = float(r[0]+r[1])/2 - - # Set the initial gain control - self.set_gain(options.gain) - - if not(self.set_freq(options.freq)): - self._set_status_msg("Failed to set initial frequency") - - # Set declination - self.set_decln (self.decln) - - - # RF hardware information - self.myform['decim'].set_value(self.u.decim_rate()) - self.myform['USB BW'].set_value(self.u.adc_freq() / self.u.decim_rate()) - if (self.dual_mode == True): - self.myform['dbname'].set_value(self.subdev[0].name()+'/'+self.subdev[1].name()) - else: - self.myform['dbname'].set_value(self.subdev[0].name()) - - # Set analog baseband filtering, if DBS_RX - if self.cardtype == usrp_dbid.DBS_RX: - lbw = (self.u.adc_freq() / self.u.decim_rate()) / 2 - if lbw < 1.0e6: - lbw = 1.0e6 - self.subdev[0].set_bw(lbw) - self.subdev[1].set_bw(lbw) - - # Start the timer for the LMST display and datalogging - self.lmst_timer.Start(1000) - if (self.switch_mode == True): - self.other_timer.Start(330) - - - def _set_status_msg(self, msg): - self.frame.GetStatusBar().SetStatusText(msg, 0) - - def _build_gui(self, vbox): - - def _form_set_freq(kv): - # Adjust current SETI frequency, and limits - self.setifreq_lower = kv['freq'] - (self.seti_freq_range/2) - self.setifreq_current = kv['freq'] - self.setifreq_upper = kv['freq'] + (self.seti_freq_range/2) - - # Reset SETI analysis timer - self.seti_then = time.time() - # Zero-out hits array when changing frequency - self.hits_array[:,:] = 0.0 - self.hit_intensities[:,:] = -60.0 - - return self.set_freq(kv['freq']) - - def _form_set_decln(kv): - return self.set_decln(kv['decln']) - - # Position the FFT display - vbox.Add(self.scope.win, 15, wx.EXPAND) - - if self.setimode == False: - # Position the Total-power stripchart - vbox.Add(self.chart.win, 15, wx.EXPAND) - - # add control area at the bottom - self.myform = myform = form.form() - hbox = wx.BoxSizer(wx.HORIZONTAL) - hbox.Add((7,0), 0, wx.EXPAND) - vbox1 = wx.BoxSizer(wx.VERTICAL) - myform['freq'] = form.float_field( - parent=self.panel, sizer=vbox1, label="Center freq", weight=1, - callback=myform.check_input_and_call(_form_set_freq, self._set_status_msg)) - - vbox1.Add((4,0), 0, 0) - - myform['lmst_high'] = form.static_text_field( - parent=self.panel, sizer=vbox1, label="Current LMST", weight=1) - vbox1.Add((4,0), 0, 0) - - if self.setimode == False: - myform['spec_data'] = form.static_text_field( - parent=self.panel, sizer=vbox1, label="Spectral Cursor", weight=1) - vbox1.Add((4,0), 0, 0) - - vbox2 = wx.BoxSizer(wx.VERTICAL) - if self.setimode == False: - vbox3 = wx.BoxSizer(wx.VERTICAL) - g = self.subdev[0].gain_range() - myform['gain'] = form.slider_field(parent=self.panel, sizer=vbox2, label="RF Gain", - weight=1, - min=int(g[0]), max=int(g[1]), - callback=self.set_gain) - - vbox2.Add((4,0), 0, 0) - if self.setimode == True: - max_savg = 100 - else: - max_savg = 3000 - myform['average'] = form.slider_field(parent=self.panel, sizer=vbox2, - label="Spectral Averaging (FFT frames)", weight=1, min=1, max=max_savg, callback=self.set_averaging) - - # Set up scan control button when in SETI mode - if (self.setimode == True): - # SETI scanning control - buttonbox = wx.BoxSizer(wx.HORIZONTAL) - self.scan_control = form.button_with_callback(self.panel, - label="Scan: On ", - callback=self.toggle_scanning) - - buttonbox.Add(self.scan_control, 0, wx.CENTER) - vbox2.Add(buttonbox, 0, wx.CENTER) - - vbox2.Add((4,0), 0, 0) - - if self.setimode == False: - myform['integration'] = form.slider_field(parent=self.panel, sizer=vbox2, - label="Continuum Integration Time (sec)", weight=1, min=1, max=180, callback=self.set_integration) - - vbox2.Add((4,0), 0, 0) - - myform['decln'] = form.float_field( - parent=self.panel, sizer=vbox2, label="Current Declination", weight=1, - callback=myform.check_input_and_call(_form_set_decln)) - vbox2.Add((4,0), 0, 0) - - if self.setimode == False: - myform['offset'] = form.slider_field(parent=self.panel, sizer=vbox3, - label="Post-Detector Offset", weight=1, min=-750, max=750, - callback=self.set_pd_offset) - vbox3.Add((2,0), 0, 0) - myform['dcgain'] = form.slider_field(parent=self.panel, sizer=vbox3, - label="Post-Detector Gain", weight=1, min=1, max=100, - callback=self.set_pd_gain) - vbox3.Add((2,0), 0, 0) - hbox.Add(vbox1, 0, 0) - hbox.Add(vbox2, wx.ALIGN_RIGHT, 0) - - if self.setimode == False: - hbox.Add(vbox3, wx.ALIGN_RIGHT, 0) - - vbox.Add(hbox, 0, wx.EXPAND) - - self._build_subpanel(vbox) - - self.lmst_timer = wx.PyTimer(self.lmst_timeout) - self.other_timer = wx.PyTimer(self.other_timeout) - - - def _build_subpanel(self, vbox_arg): - # build a secondary information panel (sometimes hidden) - - # FIXME figure out how to have this be a subpanel that is always - # created, but has its visibility controlled by foo.Show(True/False) - - if not(self.show_debug_info): - return - - panel = self.panel - vbox = vbox_arg - myform = self.myform - - #panel = wx.Panel(self.panel, -1) - #vbox = wx.BoxSizer(wx.VERTICAL) - - hbox = wx.BoxSizer(wx.HORIZONTAL) - hbox.Add((5,0), 0) - myform['decim'] = form.static_float_field( - parent=panel, sizer=hbox, label="Decim") - - hbox.Add((5,0), 1) - myform['USB BW'] = form.static_float_field( - parent=panel, sizer=hbox, label="USB BW") - - hbox.Add((5,0), 1) - myform['dbname'] = form.static_text_field( - parent=panel, sizer=hbox) - - hbox.Add((5,0), 1) - myform['baseband'] = form.static_float_field( - parent=panel, sizer=hbox, label="Analog BB") - - hbox.Add((5,0), 1) - myform['ddc'] = form.static_float_field( - parent=panel, sizer=hbox, label="DDC") - - hbox.Add((5,0), 0) - vbox.Add(hbox, 0, wx.EXPAND) - - - - def set_freq(self, target_freq): - """ - Set the center frequency we're interested in. - - @param target_freq: frequency in Hz - - """ - # - # - r = usrp.tune(self.u, self.subdev[0].which(), self.subdev[0], target_freq) - r = usrp.tune(self.u, self.subdev[1].which(), self.subdev[1], target_freq) - if r: - self.myform['freq'].set_value(target_freq) # update displayed value - # - # Make sure calibrator knows our target freq - # - - # Remember centerfreq---used for doppler calcs - delta = self.centerfreq - target_freq - self.centerfreq = target_freq - self.observing -= delta - self.scope.set_baseband_freq (self.observing) - self.myform['baseband'].set_value(r.baseband_freq) - self.myform['ddc'].set_value(r.dxc_freq) - - if (self.use_notches): - self.compute_notch_taps(self.notches) - if self.dual_mode == False and self.interferometer == False: - self.notch_filt.set_taps(self.notch_taps) - else: - self.notch_filt1.set_taps(self.notch_taps) - self.notch_filt2.set_taps(self.notch_taps) - - return True - - return False - - def set_decln(self, dec): - self.decln = dec - self.myform['decln'].set_value(dec) # update displayed value - - def set_gain(self, gain): - self.myform['gain'].set_value(gain) # update displayed value - self.subdev[0].set_gain(gain) - self.subdev[1].set_gain(gain) - self.gain = gain - - def set_averaging(self, avval): - self.myform['average'].set_value(avval) - self.scope.set_avg_alpha(1.0/(avval)) - self.scope.set_average(True) - self.avg_alpha = avval - - def set_integration(self, integval): - if self.setimode == False: - self.integrator.set_taps(1.0/((integval)*(self.bw/2))) - self.myform['integration'].set_value(integval) - self.integ = integval - - # - # Timeout function - # Used to update LMST display, as well as current - # continuum value - # - # We also write external data-logging files here - # - def lmst_timeout(self): - self.locality.date = ephem.now() - if self.setimode == False: - x = self.probe.level() - sidtime = self.locality.sidereal_time() - # LMST - s = str(ephem.hours(sidtime)) + " " + self.sunstate - # Continuum detector value - if self.setimode == False: - sx = "%7.4f" % x - s = s + "\nDet: " + str(sx) - else: - sx = "%2d" % self.hitcounter - s1 = "%2d" % self.s1hitcounter - s2 = "%2d" % self.s2hitcounter - sa = "%4.2f" % self.avgdelta - sy = "%3.1f-%3.1f" % (self.CHIRP_LOWER, self.CHIRP_UPPER) - s = s + "\nHits: " + str(sx) + "\nS1:" + str(s1) + " S2:" + str(s2) - s = s + "\nAv D: " + str(sa) + "\nCh lim: " + str(sy) - - self.myform['lmst_high'].set_value(s) - - # - # Write data out to recording files - # - if self.setimode == False: - self.write_continuum_data(x,sidtime) - self.write_spectral_data(self.fft_outbuf,sidtime) - - else: - self.seti_analysis(self.fft_outbuf,sidtime) - now = time.time() - if ((self.scanning == True) and ((now - self.seti_then) > self.setifreq_timer)): - self.seti_then = now - self.setifreq_current = self.setifreq_current + self.fft_input_rate - if (self.setifreq_current > self.setifreq_upper): - self.setifreq_current = self.setifreq_lower - self.set_freq(self.setifreq_current) - # Make sure we zero-out the hits array when changing - # frequency. - self.hits_array[:,:] = 0.0 - self.hit_intensities[:,:] = 0.0 - - def other_timeout(self): - if (self.switch_state == 0): - self.switch_state = 1 - - elif (self.switch_state == 1): - self.switch_state = 0 - - if (self.switch_state == 0): - self.mute.set_n(1) - self.cmute.set_n(int(1.0e9)) - - elif (self.switch_state == 1): - self.mute.set_n(int(1.0e9)) - self.cmute.set_n(1) - - if (self.ref_fifo != "@@@@"): - self.ref_fifo_file.write(str(self.switch_state)+"\n") - self.ref_fifo_file.flush() - - self.avg_reference_value = self.cprobe.level() - - # - # Set reference value - # - self.reference_level.set_k(-1.0 * (self.avg_reference_value/self.reference_divisor)) - - def fft_outfunc(self,data,l): - self.fft_outbuf=data - - def write_continuum_data(self,data,sidtime): - - # Create localtime structure for producing filename - foo = time.localtime() - pfx = self.prefix - filenamestr = "%s/%04d%02d%02d%02d" % (pfx, foo.tm_year, - foo.tm_mon, foo.tm_mday, foo.tm_hour) - - # Open the data file, appending - continuum_file = open (filenamestr+".tpdat","a") - - flt = "%6.3f" % data - inter = self.decln - integ = self.integ - fc = self.observing - fc = fc / 1000000 - bw = self.bw - bw = bw / 1000000 - ga = self.gain - - now = time.time() - - # - # If time to write full header info (saves storage this way) - # - if (now - self.continuum_then > 20): - self.sun.compute(self.locality) - enow = ephem.now() - sunset = self.locality.next_setting(self.sun) - sunrise = self.locality.next_rising(self.sun) - sun_insky = "Down" - self.sunstate = "Dn" - if ((sunrise < enow) and (enow < sunset)): - sun_insky = "Up" - self.sunstate = "Up" - self.continuum_then = now - - continuum_file.write(str(ephem.hours(sidtime))+" "+flt+" Dn="+str(inter)+",") - continuum_file.write("Ti="+str(integ)+",Fc="+str(fc)+",Bw="+str(bw)) - continuum_file.write(",Ga="+str(ga)+",Sun="+str(sun_insky)+"\n") - else: - continuum_file.write(str(ephem.hours(sidtime))+" "+flt+"\n") - - continuum_file.close() - return(data) - - def write_spectral_data(self,data,sidtime): - - now = time.time() - delta = 10 - - # If time to write out spectral data - # We don't write this out every time, in order to - # save disk space. Since the spectral data are - # typically heavily averaged, writing this data - # "once in a while" is OK. - # - if (now - self.spectral_then >= delta): - self.spectral_then = now - - # Get localtime structure to make filename from - foo = time.localtime() - - pfx = self.prefix - filenamestr = "%s/%04d%02d%02d%02d" % (pfx, foo.tm_year, - foo.tm_mon, foo.tm_mday, foo.tm_hour) - - # Open the file - spectral_file = open (filenamestr+".sdat","a") - - # Setup data fields to be written - r = data - inter = self.decln - fc = self.observing - fc = fc / 1000000 - bw = self.bw - bw = bw / 1000000 - av = self.avg_alpha - - # Write those fields - spectral_file.write("data:"+str(ephem.hours(sidtime))+" Dn="+str(inter)+",Fc="+str(fc)+",Bw="+str(bw)+",Av="+str(av)) - spectral_file.write (" [ ") - for r in data: - spectral_file.write(" "+str(r)) - - spectral_file.write(" ]\n") - spectral_file.close() - return(data) - - return(data) - - def seti_analysis(self,fftbuf,sidtime): - l = len(fftbuf) - x = 0 - hits = [] - hit_intensities = [] - if self.seticounter < self.setitimer: - self.seticounter = self.seticounter + 1 - return - else: - self.seticounter = 0 - - # Run through FFT output buffer, computing standard deviation (Sigma) - avg = 0 - # First compute average - for i in range(0,l): - avg = avg + fftbuf[i] - avg = avg / l - - sigma = 0.0 - # Then compute standard deviation (Sigma) - for i in range(0,l): - d = fftbuf[i] - avg - sigma = sigma + (d*d) - - sigma = Numeric.sqrt(sigma/l) - - # - # Snarfle through the FFT output buffer again, looking for - # outlying data points - - start_f = self.observing - (self.fft_input_rate/2) - current_f = start_f - l = len(fftbuf) - f_incr = self.fft_input_rate / l - hit = -1 - - # -nyquist to DC - for i in range(l/2,l): - # - # If current FFT buffer has an item that exceeds the specified - # sigma - # - if ((fftbuf[i] - avg) > (self.setik * sigma)): - hits.append(current_f) - hit_intensities.append(fftbuf[i]) - current_f = current_f + f_incr - - # DC to nyquist - for i in range(0,l/2): - # - # If current FFT buffer has an item that exceeds the specified - # sigma - # - if ((fftbuf[i] - avg) > (self.setik * sigma)): - hits.append(current_f) - hit_intensities.append(fftbuf[i]) - current_f = current_f + f_incr - - # No hits - if (len(hits) <= 0): - return - - - # - # OK, so we have some hits in the FFT buffer - # They'll have a rather substantial gauntlet to run before - # being declared a real "hit" - # - - # Update stats - self.s1hitcounter = self.s1hitcounter + len(hits) - - # Weed out buffers with an excessive number of - # signals above Sigma - if (len(hits) > self.nhits): - return - - - # Weed out FFT buffers with apparent multiple narrowband signals - # separated significantly in frequency. This means that a - # single signal spanning multiple bins is OK, but a buffer that - # has multiple, apparently-separate, signals isn't OK. - # - last = hits[0] - ns2 = 1 - for i in range(1,len(hits)): - if ((hits[i] - last) > (f_incr*3.0)): - return - last = hits[i] - ns2 = ns2 + 1 - - self.s2hitcounter = self.s2hitcounter + ns2 - - # - # Run through all available hit buffers, computing difference between - # frequencies found there, if they're all within the chirp limits - # declare a good hit - # - good_hit = False - f_ds = Numeric.zeros(self.nhitlines, Numeric.Float64) - avg_delta = 0 - k = 0 - for i in range(0,min(len(hits),len(self.hits_array[:,0]))): - f_ds[0] = abs(self.hits_array[i,0] - hits[i]) - for j in range(1,len(f_ds)): - f_ds[j] = abs(self.hits_array[i,j] - self.hits_array[i,0]) - avg_delta = avg_delta + f_ds[j] - k = k + 1 - - if (self.seti_isahit (f_ds)): - good_hit = True - self.hitcounter = self.hitcounter + 1 - break - - if (avg_delta/k < (self.seti_fft_bandwidth/2)): - self.avgdelta = avg_delta / k - - # Save 'n shuffle hits - # Old hit buffers percolate through the hit buffers - # (there are self.nhitlines of these buffers) - # and then drop off the end - # A consequence is that while the nhitlines buffers are filling, - # you can get some absurd values for self.avgdelta, because some - # of the buffers are full of zeros - for i in range(self.nhitlines,1): - self.hits_array[:,i] = self.hits_array[:,i-1] - self.hit_intensities[:,i] = self.hit_intensities[:,i-1] - - for i in range(0,len(hits)): - self.hits_array[i,0] = hits[i] - self.hit_intensities[i,0] = hit_intensities[i] - - # Finally, write the hits/intensities buffer - if (good_hit): - self.write_hits(sidtime) - - return - - def seti_isahit(self,fdiffs): - truecount = 0 - - for i in range(0,len(fdiffs)): - if (fdiffs[i] >= self.CHIRP_LOWER and fdiffs[i] <= self.CHIRP_UPPER): - truecount = truecount + 1 - - if truecount == len(fdiffs): - return (True) - else: - return (False) - - def write_hits(self,sidtime): - # Create localtime structure for producing filename - foo = time.localtime() - pfx = self.prefix - filenamestr = "%s/%04d%02d%02d%02d" % (pfx, foo.tm_year, - foo.tm_mon, foo.tm_mday, foo.tm_hour) - - # Open the data file, appending - hits_file = open (filenamestr+".seti","a") - - # Write sidtime first - hits_file.write(str(ephem.hours(sidtime))+" "+str(self.decln)+" ") - - # - # Then write the hits/hit intensities buffers with enough - # "syntax" to allow parsing by external (not yet written!) - # "stuff". - # - for i in range(0,self.nhitlines): - hits_file.write(" ") - for j in range(0,self.nhits): - hits_file.write(str(self.hits_array[j,i])+":") - hits_file.write(str(self.hit_intensities[j,i])+",") - hits_file.write("\n") - hits_file.close() - return - - def xydfunc(self,func,xyv): - if self.setimode == True: - return - magn = int(Numeric.log10(self.observing)) - if (magn == 6 or magn == 7 or magn == 8): - magn = 6 - dfreq = xyv[0] * pow(10.0,magn) - if func == 0: - ratio = self.observing / dfreq - vs = 1.0 - ratio - vs *= 299792.0 - if magn >= 9: - xhz = "Ghz" - elif magn >= 6: - xhz = "Mhz" - elif magn <= 5: - xhz = "Khz" - s = "%.6f%s\n%.3fdB" % (xyv[0], xhz, xyv[1]) - s2 = "\n%.3fkm/s" % vs - self.myform['spec_data'].set_value(s+s2) - else: - tmpnotches = Numeric.zeros(self.NOTCH_TAPS,Numeric.Float64) - delfreq = -1 - if self.use_notches == True: - for i in range(0,len(self.notches)): - if self.notches[i] != 0 and abs(self.notches[i] - dfreq) < ((self.bw/self.NOTCH_TAPS)/2.0): - delfreq = i - break - j = 0 - for i in range(0,len(self.notches)): - if (i != delfreq): - tmpnotches[j] = self.notches[i] - j = j + 1 - if (delfreq == -1): - for i in range(0,len(tmpnotches)): - if (int(tmpnotches[i]) == 0): - tmpnotches[i] = dfreq - break - self.notches = tmpnotches - self.compute_notch_taps(self.notches) - if self.dual_mode == False and self.interferometer == False: - self.notch_filt.set_taps(self.notch_taps) - else: - self.notch_filt1.set_taps(self.notch_taps) - self.notch_filt2.set_taps(self.notch_taps) - - def xydfunc_waterfall(self,pos): - lower = self.observing - (self.seti_fft_bandwidth / 2) - upper = self.observing + (self.seti_fft_bandwidth / 2) - binwidth = self.seti_fft_bandwidth / 1024 - s = "%.6fMHz" % ((lower + (pos.x*binwidth)) / 1.0e6) - self.myform['spec_data'].set_value(s) - - def toggle_cal(self): - if (self.calstate == True): - self.calstate = False - self.u.write_io(0,0,(1<<15)) - self.calibrator.SetLabel("Calibration Source: Off") - else: - self.calstate = True - self.u.write_io(0,(1<<15),(1<<15)) - self.calibrator.SetLabel("Calibration Source: On") - - def toggle_annotation(self): - if (self.annotate_state == True): - self.annotate_state = False - self.annotation.SetLabel("Annotation: Off") - else: - self.annotate_state = True - self.annotation.SetLabel("Annotation: On") - # - # Turn scanning on/off - # Called-back by "Recording" button - # - def toggle_scanning(self): - # Current scanning? Flip state - if (self.scanning == True): - self.scanning = False - self.scan_control.SetLabel("Scan: Off") - # Not scanning - else: - self.scanning = True - self.scan_control.SetLabel("Scan: On ") - - def set_pd_offset(self,offs): - self.myform['offset'].set_value(offs) - self.calib_offset=offs - x = self.calib_coeff / 100.0 - self.cal_offs.set_k(offs*(x*8000)) - - def set_pd_gain(self,gain): - self.myform['dcgain'].set_value(gain) - self.cal_mult.set_k(gain*0.01) - self.calib_coeff = gain - x = gain/100.0 - self.cal_offs.set_k(self.calib_offset*(x*8000)) - - def compute_notch_taps(self,notchlist): - tmptaps = Numeric.zeros(self.NOTCH_TAPS,Numeric.Complex64) - binwidth = self.bw / self.NOTCH_TAPS - - for i in range(0,self.NOTCH_TAPS): - tmptaps[i] = complex(1.0,0.0) - - for i in notchlist: - diff = i - self.observing - if int(i) == 0: - break - if ((i < (self.observing - self.bw/2)) or (i > (self.observing + self.bw/2))): - continue - if (diff > 0): - idx = diff / binwidth - idx = round(idx) - idx = int(idx) - if (idx < 0 or idx > (self.NOTCH_TAPS/2)): - break - tmptaps[idx] = complex(0.0, 0.0) - - if (diff < 0): - idx = -diff / binwidth - idx = round(idx) - idx = (self.NOTCH_TAPS/2) - idx - idx = int(idx+(self.NOTCH_TAPS/2)) - if (idx < 0 or idx >= (self.NOTCH_TAPS)): - break - tmptaps[idx] = complex(0.0, 0.0) - - self.notch_taps = numpy.fft.ifft(tmptaps) - - # - # Setup common pieces of radiometer mode - # - def setup_radiometer_common(self,n): - # The IIR integration filter for post-detection - self.integrator = gr.single_pole_iir_filter_ff(1.0) - self.integrator.set_taps (1.0/self.bw) - - if (self.use_notches == True): - self.compute_notch_taps(self.notches) - if (n == 2): - self.notch_filt1 = gr.fft_filter_ccc(1, self.notch_taps) - self.notch_filt2 = gr.fft_filter_ccc(1, self.notch_taps) - else: - self.notch_filt = gr.fft_filter_ccc(1, self.notch_taps) - - - # Signal probe - self.probe = gr.probe_signal_f() - - # - # Continuum calibration stuff - # - x = self.calib_coeff/100.0 - self.cal_mult = gr.multiply_const_ff(self.calib_coeff/100.0) - self.cal_offs = gr.add_const_ff(self.calib_offset*(x*8000)) - - # - # Mega decimator after IIR filter - # - if (self.switch_mode == False): - self.keepn = gr.keep_one_in_n(gr.sizeof_float, self.bw) - else: - self.keepn = gr.keep_one_in_n(gr.sizeof_float, int(self.bw/2)) - - # - # For the Dicke-switching scheme - # - #self.switch = gr.multiply_const_ff(1.0) - - # - if (self.switch_mode == True): - self.vector = gr.vector_sink_f() - self.swkeep = gr.keep_one_in_n(gr.sizeof_float, int(self.bw/3)) - self.mute = gr.keep_one_in_n(gr.sizeof_float, 1) - self.cmute = gr.keep_one_in_n(gr.sizeof_float, int(1.0e9)) - self.cintegrator = gr.single_pole_iir_filter_ff(1.0/(self.bw/2)) - self.cprobe = gr.probe_signal_f() - else: - self.mute = gr.multiply_const_ff(1.0) - - - self.avg_reference_value = 0.0 - self.reference_level = gr.add_const_ff(0.0) - - # - # Setup ordinary single-channel radiometer mode - # - def setup_normal(self, setimode): - - self.setup_radiometer_common(1) - - self.head = self.u - if (self.use_notches == True): - self.shead = self.notch_filt - else: - self.shead = self.u - - if setimode == False: - - self.detector = gr.complex_to_mag_squared() - self.connect(self.shead, self.scope) - - if (self.use_notches == False): - self.connect(self.head, self.detector, self.mute, self.reference_level, - self.integrator, self.keepn, self.cal_mult, self.cal_offs, self.chart) - else: - self.connect(self.head, self.notch_filt, self.detector, self.mute, self.reference_level, - self.integrator, self.keepn, self.cal_mult, self.cal_offs, self.chart) - - self.connect(self.cal_offs, self.probe) - - # - # Add a side-chain detector chain, with a different integrator, for sampling - # The reference channel data - # This is used to derive the offset value for self.reference_level, used above - # - if (self.switch_mode == True): - self.connect(self.detector, self.cmute, self.cintegrator, self.swkeep, self.cprobe) - - return - - # - # Setup dual-channel (two antenna, usual orthogonal polarity probes in the same waveguide) - # - def setup_dual(self, setimode): - - self.setup_radiometer_common(2) - - self.di = gr.deinterleave(gr.sizeof_gr_complex) - self.addchans = gr.add_cc () - self.detector = gr.add_ff () - self.h_power = gr.complex_to_mag_squared() - self.v_power = gr.complex_to_mag_squared() - self.connect (self.u, self.di) - - if (self.use_notches == True): - self.connect((self.di, 0), self.notch_filt1, (self.addchans, 0)) - self.connect((self.di, 1), self.notch_filt2, (self.addchans, 1)) - else: - # - # For spectral, adding the two channels works, assuming no gross - # phase or amplitude error - self.connect ((self.di, 0), (self.addchans, 0)) - self.connect ((self.di, 1), (self.addchans, 1)) - - # - # Connect heads of spectral and total-power chains - # - if (self.use_notches == False): - self.head = self.di - else: - self.head = (self.notch_filt1, self.notch_filt2) - - self.shead = self.addchans - - if (setimode == False): - # - # For dual-polarization mode, we compute the sum of the - # powers on each channel, after they've been detected - # - self.detector = gr.add_ff() - - # - # In dual-polarization mode, we compute things a little differently - # In effect, we have two radiometer chains, terminating in an adder - # - if self.use_notches == True: - self.connect(self.notch_filt1, self.h_power) - self.connect(self.notch_filt2, self.v_power) - else: - self.connect((self.head, 0), self.h_power) - self.connect((self.head, 1), self.v_power) - self.connect(self.h_power, (self.detector, 0)) - self.connect(self.v_power, (self.detector, 1)) - self.connect(self.detector, self.mute, self.reference_level, - self.integrator, self.keepn, self.cal_mult, self.cal_offs, self.chart) - self.connect(self.cal_offs, self.probe) - self.connect(self.shead, self.scope) - - # - # Add a side-chain detector chain, with a different integrator, for sampling - # The reference channel data - # This is used to derive the offset value for self.reference_level, used above - # - if (self.switch_mode == True): - self.connect(self.detector, self.cmute, self.cintegrator, self.swkeep, self.cprobe) - return - - # - # Setup correlating interferometer mode - # - def setup_interferometer(self, setimode): - self.setup_radiometer_common(2) - - self.di = gr.deinterleave(gr.sizeof_gr_complex) - self.connect (self.u, self.di) - self.corr = gr.multiply_cc() - self.c2f = gr.complex_to_float() - - self.shead = (self.di, 0) - - # Channel 0 to multiply port 0 - # Channel 1 to multiply port 1 - if (self.use_notches == False): - self.connect((self.di, 0), (self.corr, 0)) - self.connect((self.di, 1), (self.corr, 1)) - else: - self.connect((self.di, 0), self.notch_filt1, (self.corr, 0)) - self.connect((self.di, 1), self.notch_filt2, (self.corr, 0)) - - # - # Multiplier (correlator) to complex-to-float, followed by integrator, etc - # - self.connect(self.corr, self.c2f, self.integrator, self.keepn, self.cal_mult, self.cal_offs, self.chart) - - # - # FFT scope gets only 1 channel - # FIX THIS, by cross-correlating the *outputs* of two different FFTs, then display - # Funky! - # - self.connect(self.shead, self.scope) - - # - # Output of correlator/integrator chain to probe - # - self.connect(self.cal_offs, self.probe) - - return - - # - # Setup SETI mode - # - def setup_seti(self): - self.connect (self.shead, self.fft_bandpass, self.scope) - return - - def setup_usrp(self): - - if (self.usrp2 == False): - if (self.dual_mode == False and self.interferometer == False): - if (self.decim > 4): - self.u = usrp.source_c(decim_rate=self.decim,fusb_block_size=8192) - else: - self.u = usrp.source_c(decim_rate=self.decim,fusb_block_size=8192, fpga_filename="std_4rx_0tx.rbf") - self.u.set_mux(usrp.determine_rx_mux_value(self.u, self.rx_subdev_spec)) - # determine the daughterboard subdevice we're using - self.subdev[0] = usrp.selected_subdev(self.u, self.rx_subdev_spec) - self.subdev[1] = self.subdev[0] - self.cardtype = self.subdev[0].dbid() - else: - self.u=usrp.source_c(decim_rate=self.decim, nchan=2,fusb_block_size=8192) - self.subdev[0] = usrp.selected_subdev(self.u, (0, 0)) - self.subdev[1] = usrp.selected_subdev(self.u, (1, 0)) - self.cardtype = self.subdev[0].dbid() - self.u.set_mux(0x32103210) - c1 = self.subdev[0].name() - c2 = self.subdev[1].name() - if (c1 != c2): - print "Must have identical cardtypes for --dual_mode or --interferometer" - sys.exit(1) - # - # Set 8-bit mode - # - - width = 8 - shift = 8 - format = self.u.make_format(width, shift) - r = self.u.set_format(format) - else: - if (self.dual_mode == True or self.interferometer == True): - print "Cannot use dual_mode or interferometer with single USRP2" - sys.exit(1) - self.u = usrp2.source_32fc(self.interface, self.mac_addr) - self.u.set_decim (self.decim) - self.cardtype = self.u.daughterboard_id() - -def main (): - app = stdgui2.stdapp(app_flow_graph, "RADIO ASTRONOMY SPECTRAL/CONTINUUM RECEIVER: $Revision$", nstatus=1) - app.MainLoop() - -if __name__ == '__main__': - main () |