From 24c3d69446455800e0ba839580d36a7a7cd962cc Mon Sep 17 00:00:00 2001 From: jcorgan Date: Fri, 29 Feb 2008 19:26:49 +0000 Subject: [PATCH] Merged r7857:7898 from n4hy/msddLTS into trunk, with modification. Adds gr-msdd6000 component, providing GNU Radio source and sink support for the Softronics Ltd. MSDD 6000 receiver. git-svn-id: http://gnuradio.org/svn/gnuradio/trunk@7904 221aa14e-8319-0410-a670-987f0aec2ac5 --- AUTHORS | 1 + config/Makefile.am | 1 + config/grc_gr_msdd6000.m4 | 38 + configure.ac | 1 + gr-msdd6000/AUTHORS | 3 + gr-msdd6000/Makefile.am | 25 + .../Softronics_Ltd_msdd6000_BlockDiagram.pdf | Bin 0 -> 43889 bytes gr-msdd6000/src/Makefile.am | 22 + gr-msdd6000/src/lib/Makefile.am | 117 +++ gr-msdd6000/src/lib/msdd.i | 254 ++++++ .../src/lib/msdd_buffer_copy_behaviors.h | 59 ++ gr-msdd6000/src/lib/msdd_source_base.cc | 817 ++++++++++++++++++ gr-msdd6000/src/lib/msdd_source_base.h | 307 +++++++ gr-msdd6000/src/lib/msdd_source_c.cc | 111 +++ gr-msdd6000/src/lib/msdd_source_c.h | 79 ++ gr-msdd6000/src/lib/msdd_source_s.cc | 130 +++ gr-msdd6000/src/lib/msdd_source_s.h | 87 ++ gr-msdd6000/src/python/Makefile.am | 48 + gr-msdd6000/src/python/qa_msdd6000.py | 40 + gr-msdd6000/src/python/run_tests.in | 10 + gr-msdd6000/src/python/test_tcp.py | 78 ++ gr-msdd6000/src/python/test_udp.py | 40 + gr-msdd6000/src/python_test/test_tcp.py | 78 ++ gr-msdd6000/src/python_test/test_udp.py | 40 + 24 files changed, 2386 insertions(+) create mode 100644 config/grc_gr_msdd6000.m4 create mode 100644 gr-msdd6000/AUTHORS create mode 100644 gr-msdd6000/Makefile.am create mode 100644 gr-msdd6000/doc/Softronics_Ltd_msdd6000_BlockDiagram.pdf create mode 100644 gr-msdd6000/src/Makefile.am create mode 100644 gr-msdd6000/src/lib/Makefile.am create mode 100644 gr-msdd6000/src/lib/msdd.i create mode 100644 gr-msdd6000/src/lib/msdd_buffer_copy_behaviors.h create mode 100644 gr-msdd6000/src/lib/msdd_source_base.cc create mode 100644 gr-msdd6000/src/lib/msdd_source_base.h create mode 100644 gr-msdd6000/src/lib/msdd_source_c.cc create mode 100644 gr-msdd6000/src/lib/msdd_source_c.h create mode 100644 gr-msdd6000/src/lib/msdd_source_s.cc create mode 100644 gr-msdd6000/src/lib/msdd_source_s.h create mode 100644 gr-msdd6000/src/python/Makefile.am create mode 100755 gr-msdd6000/src/python/qa_msdd6000.py create mode 100644 gr-msdd6000/src/python/run_tests.in create mode 100755 gr-msdd6000/src/python/test_tcp.py create mode 100755 gr-msdd6000/src/python/test_udp.py create mode 100755 gr-msdd6000/src/python_test/test_tcp.py create mode 100755 gr-msdd6000/src/python_test/test_udp.py diff --git a/AUTHORS b/AUTHORS index e030f7b7..9c6e4c6c 100644 --- a/AUTHORS +++ b/AUTHORS @@ -12,3 +12,4 @@ Joshua Lackey Original GMSK implementation. Johnathan Corgan Build system, ongoing stuff, release manager Bdale Garbee Debian release packages Tom Rondeau Mostly digital waveforms and a little bit of trouble +Nate Goergen (UMD Student) diff --git a/config/Makefile.am b/config/Makefile.am index 676183aa..be258b71 100644 --- a/config/Makefile.am +++ b/config/Makefile.am @@ -57,6 +57,7 @@ m4macros = \ gr_check_usrp.m4 \ grc_pmt.m4 \ grc_usrp.m4 \ + grc_gr_msdd6000.m4 \ gr_doxygen.m4 \ gr_fortran.m4 \ gr_gprof.m4 \ diff --git a/config/grc_gr_msdd6000.m4 b/config/grc_gr_msdd6000.m4 new file mode 100644 index 00000000..056b084b --- /dev/null +++ b/config/grc_gr_msdd6000.m4 @@ -0,0 +1,38 @@ +dnl Copyright 2001,2002,2003,2004,2005,2006,2008 Free Software Foundation, Inc. +dnl +dnl This file is part of GNU Radio +dnl +dnl GNU Radio is free software; you can redistribute it and/or modify +dnl it under the terms of the GNU General Public License as published by +dnl the Free Software Foundation; either version 3, or (at your option) +dnl any later version. +dnl +dnl GNU Radio is distributed in the hope that it will be useful, +dnl but WITHOUT ANY WARRANTY; without even the implied warranty of +dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +dnl GNU General Public License for more details. +dnl +dnl You should have received a copy of the GNU General Public License +dnl along with GNU Radio; see the file COPYING. If not, write to +dnl the Free Software Foundation, Inc., 51 Franklin Street, +dnl Boston, MA 02110-1301, USA. + +AC_DEFUN([GRC_GR_MSDD6000],[ + GRC_ENABLE([gr-msdd6000]) + + AC_CONFIG_FILES([\ + gr-msdd6000/Makefile \ + gr-msdd6000/src/Makefile \ + gr-msdd6000/src/lib/Makefile \ + gr-msdd6000/src/python/Makefile + gr-msdd6000/src/python/run_tests + ]) + + dnl Don't do gr-msdd6000 if gnuradio-core skipped + GRC_CHECK_DEPENDENCY(gr-msdd6000, gnuradio-core) + + GRC_BUILD_CONDITIONAL([gr-msdd6000],[ + dnl run_tests is created from run_tests.in. Make it executable. + AC_CONFIG_COMMANDS([run_tests_msdd6000], [chmod +x gr-msdd6000/src/python/run_tests]) + ]) +]) diff --git a/configure.ac b/configure.ac index e47bcc2d..f918f9b0 100644 --- a/configure.ac +++ b/configure.ac @@ -249,6 +249,7 @@ GRC_PMT GRC_MBLOCK dnl this must come after GRC_PMT GRC_USRP GRC_GR_USRP dnl this must come after GRC_USRP +GRC_GR_MSDD6000 GRC_GR_AUDIO_ALSA GRC_GR_AUDIO_JACK GRC_GR_AUDIO_OSS diff --git a/gr-msdd6000/AUTHORS b/gr-msdd6000/AUTHORS new file mode 100644 index 00000000..683b38b5 --- /dev/null +++ b/gr-msdd6000/AUTHORS @@ -0,0 +1,3 @@ +Charles Clancy +Nate Goergen +n4hy (Bob McGwier) diff --git a/gr-msdd6000/Makefile.am b/gr-msdd6000/Makefile.am new file mode 100644 index 00000000..73f418ed --- /dev/null +++ b/gr-msdd6000/Makefile.am @@ -0,0 +1,25 @@ +# +# Copyright 2008 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. +# + +include $(top_srcdir)/Makefile.common + +SUBDIRS = src + diff --git a/gr-msdd6000/doc/Softronics_Ltd_msdd6000_BlockDiagram.pdf b/gr-msdd6000/doc/Softronics_Ltd_msdd6000_BlockDiagram.pdf new file mode 100644 index 0000000000000000000000000000000000000000..34356da75c8a7b97f4879faa6cd7c300ec79774d GIT binary patch literal 43889 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 z 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%0Dtf6#R*E&YbeS_9HK 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 ak`N!TIC~847$rCiij<(_=T|dOr~Dr~^8=Cq literal 0 HcmV?d00001 diff --git a/gr-msdd6000/src/Makefile.am b/gr-msdd6000/src/Makefile.am new file mode 100644 index 00000000..923b1c25 --- /dev/null +++ b/gr-msdd6000/src/Makefile.am @@ -0,0 +1,22 @@ +# +# Copyright 2004,2005,2006 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 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. +# + +SUBDIRS = lib python diff --git a/gr-msdd6000/src/lib/Makefile.am b/gr-msdd6000/src/lib/Makefile.am new file mode 100644 index 00000000..00018902 --- /dev/null +++ b/gr-msdd6000/src/lib/Makefile.am @@ -0,0 +1,117 @@ +# +# Copyright 2008 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. +# + + +include $(top_srcdir)/Makefile.common + +# Install this stuff so that it ends up as the gnuradio.msdd module +# This usually ends up at: +# ${prefix}/lib/python${python_version}/site-packages/gnuradio + +ourpythondir = $(grpythondir) +ourlibdir = $(grpyexecdir) + +AM_CPPFLAGS = \ + $(STD_DEFINES_AND_INCLUDES) \ + $(PYTHON_CPPFLAGS) \ + $(WITH_INCLUDES) \ + $(CPPUNIT_INCLUDES) + +noinst_LTLIBRARIES = libmsdd.la + +libmsdd_la_SOURCES = \ + msdd_source_base.cc \ + msdd_source_c.cc \ + msdd_source_s.cc + +# ------------------------------------------------------------------------ +# This is the swig-ish part of the Makefile. +# It builds the msdd module which we'll load into python +# ------------------------------------------------------------------------ + +SWIGCPPPYTHONARGS = -fvirtual -python -modern $(PYTHON_CPPFLAGS) \ + $(STD_DEFINES_AND_INCLUDES) $(WITH_SWIG_INCLUDES) $(WITH_INCLUDES) + +ALL_IFILES = \ + $(LOCAL_IFILES) \ + $(NON_LOCAL_IFILES) + +NON_LOCAL_IFILES = \ + $(top_srcdir)/gnuradio-core/src/lib/swig/gnuradio.i + +LOCAL_IFILES = \ + $(top_srcdir)/gr-msdd6000/src/lib/msdd.i + + +# These files are built by SWIG. The first is the C++ glue. +# # The second is the python wrapper that loads the _msdd shared library +# # and knows how to call our extensions. +# +# +# +swig_built_sources = \ + msdd.cc \ + msdd.py + +# This gets msdd.py installed in the right place +# +ourpython_PYTHON = \ + msdd.py + + +ourlib_LTLIBRARIES = _msdd.la + +# These are the source files that go into the shared library +_msdd_la_SOURCES = \ + msdd.cc + +# magic flag +_msdd_la_LDFLAGS = $(NO_UNDEFINED) -module -avoid-version + +# link the library against some comon swig runtime code and the +# c++ standard library + +_msdd_la_LIBADD = \ + $(GNURADIO_CORE_LA) \ + $(PYTHON_LDFLAGS) \ + libmsdd.la \ + -lstdc++ + +msdd.cc msdd.py: msdd.i $(ALL_FILES) + $(SWIG) $(SWIGCPPPYTHONARGS) -module msdd -o msdd.cc $(LOCAL_IFILES) + +# These headers get installed in ${prefix}/include/gnuradio +grinclude_HEADERS = \ + msdd_buffer_copy_behaviors.h \ + msdd_source_base.h \ + msdd_source_c.h \ + msdd_source_s.h + +# These swig headers get installed in ${prefix}/include/gnuradio/swig +swiginclude_HEADERS = \ + $(LOCAL_IFILES) + +MOSTLYCLEANFILES = $(BUILT_SOURCES) *.pyc *~ + +# Don't distribute output of swig +dist-hook: + @for file in $(BUILT_SOURCES); do echo $(RM) $(distdir)/$$file; done + @for file in $(BUILT_SOURCES); do $(RM) $(distdir)/$$file; done diff --git a/gr-msdd6000/src/lib/msdd.i b/gr-msdd6000/src/lib/msdd.i new file mode 100644 index 00000000..809445c6 --- /dev/null +++ b/gr-msdd6000/src/lib/msdd.i @@ -0,0 +1,254 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 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. + */ + +%feature("autodoc", "1"); // generate python docstrings + +%include "exception.i" +%import "gnuradio.i" // the common stuff + +%{ + +#include "gnuradio_swig_bug_workaround.h" // mandatory bug fix +#include +#include "msdd_source_s.h" +#include "msdd_source_c.h" +%} + +// ================================================================ +// abstract classes +// ================================================================ + +// ---------------------------------------------------------------- + +class msdd_source_base : public gr_sync_block { + + protected: + msdd_source_base (const std::string &name, + gr_io_signature_sptr output_signature, + int which_board, + msdd_source_base::msdd_command_type_t opp_mode, + const char *src, + unsigned short port_src + ) throw (std::runtime_error); + + /*! + * \brief return number of msdd input bytes required to produce noutput items. + */ + int ninput_bytes_reqd_for_noutput_items (int noutput_items) = 0; + + /*! + * \brief number of bytes in a low-level sample + */ + unsigned int sizeof_basic_sample() const; + + /*! + * \brief convert between native msdd format and output item format + * + * \param output_items[out] stream(s) of output items + * \param output_index[in] starting index in output_items + * \param output_items_available[in] number of empty items available at item[index] + * \param output_items_produced[out] number of items produced by copy + * \param msdd_buffer[in] source buffer + * \param msdd_buffer_length[in] number of bytes available in \p msdd_buffer + * \param bytes_read[out] number of bytes read from \p msdd_buffer + * + * The copy must consume all bytes available. That is, \p bytes_read must equal + * \p msdd_buffer_length. + */ + virtual void copy_from_msdd_buffer (gr_vector_void_star &output_items, + int output_index, + int output_items_available, + int &output_items_produced, + const void *msdd_buffer, + int msdd_buffer_length, + int &bytes_read) = 0; + + int readsock(int sockfd, unsigned char* buf, int nbytes); + + void* make_request_packet(unsigned int& size, unsigned int number_samples); + + public: + //! magic value used on alternate register read interfaces + static const int READ_FAILED = -99999; + + ~msdd_source_base (); + + int work (int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + + bool start(); + bool stop(); + + /*! + * \brief Set Programmable Gain Amplifier (PGA) + * + * \param which which D/A [0,3] + * \param gain_in_db gain value (linear in dB) + * + * gain is rounded to closest setting supported by hardware. + * Note that DAC 0 and DAC 1 share a gain setting as do DAC 2 and DAC 3. + * Setting DAC 0 affects DAC 1 and vice versa. Same with DAC 2 and DAC 3. + * + * \returns true iff sucessful. + * + * \sa pga_min(), pga_max(), pga_db_per_step() + */ + bool set_pga (int which, double gain_in_db); + + /*! + * \brief Return programmable gain amplifier gain in dB. + * + * \param which which D/A [0,3] + */ + double pga (int which) const; + + /*! + * \brief Return minimum legal PGA gain in dB. + */ + double pga_min () const; + + /*! + * \brief Return maximum legal PGA gain in dB. + */ + double pga_max () const; + + /*! + * \brief Return hardware step size of PGA (linear in dB). + */ + double pga_db_per_step () const; + + /*! + * \brief open a socket specified by the port and ip address info + * + * Opens a socket, binds to the address, and waits for a connection + * over UDP. If any of these fail, the fuction retuns the error and exits. + */ + bool open(); + + /*! + * \brief Close current socket. + * + * Shuts down read/write on the socket + */ + bool close(); + + /*! + * \brief Set decimator rate. \p rate must be EVEN and in [8, 256]. + * + * The final complex sample rate across the USB is + * adc_freq () / decim_rate () + */ + bool set_decim_rate (unsigned int rate); + + /*! + * \brief set the center frequency of the digital down converter. + * + * \p channel must be 0. \p freq is the center frequency in Hz. + * It must be in the range [-FIXME, FIXME]. The frequency specified is + * quantized. Use rx_freq to retrieve the actual value used. + */ + bool set_rx_freq (int channel, double freq); + + void set_verbose (bool verbose); + + // ACCESSORS + + unsigned int decim_rate () const; + double rx_freq (int channel) const; + int noverruns () const { return d_noverruns; } + + /*! + * \brief return the msdd's serial number. + * + * \returns non-zero length string iff successful. + */ + std::string serial_number(); + + bool set_desired_packet_size (int which, unsigned long packet_size); + + unsigned long desired_packet_size (int which) const; + +}; + + +// ================================================================ +// concrete sources +// ================================================================ + + +// ---------------------------------------------------------------- + +GR_SWIG_BLOCK_MAGIC(msdd,source_s) + +msdd_source_s_sptr +msdd_make_source_s (int which_board, + unsigned int decim_rate, + unsigned int fft_points, + double initial_rx_freq, + int opp_mode, + const char *src, + unsigned short port_src + ) throw (std::runtime_error); + + +class msdd_source_s : public msdd_source_base { +protected: + msdd_source_s (int which_board, + unsigned int decim_rate, + unsigned int fft_points, + double initial_rx_freq, + int opp_mode, + const char *src, + unsigned short port_src + ) throw (std::runtime_error); + + virtual int ninput_bytes_reqd_for_noutput_items (int noutput_items); + +public: + ~msdd_source_s (); +}; + + +GR_SWIG_BLOCK_MAGIC(msdd,source_c) + +msdd_source_c_sptr +msdd_make_source_c (int which_board, + int opp_mode, + const char *src, + unsigned short port_src + ) throw (std::runtime_error); + + +class msdd_source_c : public msdd_source_base { +protected: + msdd_source_c (int which_board, + int opp_mode, + const char *src, + unsigned short port_src + ) throw (std::runtime_error); + + virtual int ninput_bytes_reqd_for_noutput_items (int noutput_items); + +public: + ~msdd_source_c (); +}; diff --git a/gr-msdd6000/src/lib/msdd_buffer_copy_behaviors.h b/gr-msdd6000/src/lib/msdd_buffer_copy_behaviors.h new file mode 100644 index 00000000..26cf2458 --- /dev/null +++ b/gr-msdd6000/src/lib/msdd_buffer_copy_behaviors.h @@ -0,0 +1,59 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 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. + */ +#ifndef MSDD_BUFFER_COPY_BEHAVIORS_H_ +#define MSDD_BUFFER_COPY_BEHAVIORS_H_ + +namespace msdd { + + class BufferCopyBehavior + { + public: + virtual void operator() (gr_vector_void_star &a, const void * b, unsigned int output_index, unsigned int nitems) = 0; + virtual ~BufferCopyBehavior() {}; + }; + + template + class BufferCopyBehaviorGeneric : public BufferCopyBehavior { + void operator() (gr_vector_void_star &a, const void * b, unsigned int output_index, unsigned int nitems) { + Tout *out(&(reinterpret_cast(a[0]))[output_index]); // sloppy + const Tin *in(reinterpret_cast(b)); // equisloppy + + for (unsigned int i = 0; i < nitems; ++i) { + out[i] = in[i]; + } + } + }; + + template + class BufferCopyBehaviorComplex : public BufferCopyBehavior { + void operator() (gr_vector_void_star &a, const void * b, unsigned int output_index, unsigned int nitems) { + gr_complex *out(&(reinterpret_cast(a[0]))[output_index]); // sloppy + const Tin *in(reinterpret_cast(b)); // equisloppy + + for (unsigned int i = 0; i < nitems; ++i) { + out[i] = gr_complex (in[4*i+1],in[4*i+3]); + } + } + }; +} + +#endif /*MSDD_BUFFER_COPY_BEHAVIORS_H_*/ diff --git a/gr-msdd6000/src/lib/msdd_source_base.cc b/gr-msdd6000/src/lib/msdd_source_base.cc new file mode 100644 index 00000000..d9cb03e1 --- /dev/null +++ b/gr-msdd6000/src/lib/msdd_source_base.cc @@ -0,0 +1,817 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 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. + */ + +//#define MSDD_DEBUG_TRUE +//#define MSDD_DEBUG2_TRUE + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef MSDD_DEBUG_TRUE +#include +#define MSDD_DEBUG(x) std::cout << x << std::endl; +#else +#define MSDD_DEBUG(x) +#endif + + +#ifdef MSDD_DEBUG2_TRUE +#include +#define MSDD_DEBUG2(x) std::cout << x << std::endl; +#else +#define MSDD_DEBUG2(x) +#endif + +#include + +namespace { + const int OUTPUT_MAX((1 << 15)*8); + const double PGA_MAX(75); + const double PGA_MIN(10); + const double PGA_STEP(.5); + const double DEFAULT_RX_FREQ(2.417e6); + const double DEFAULT_GAIN(32); + const msdd_source_base::msdd_fft_mode_t DEFAULT_FFT_MODE(msdd_source_base::MODE_MAG); + const msdd_source_base::msdd_fft_points_t DEFAULT_FFT_POINTS(msdd_source_base::S8192); + const msdd_source_base::msdd_decimation_t DEFAULT_DECIMATION_RATE(msdd_source_base::D2); +} + +class msdd_source_base::Impl { + +public: + Impl(int opp_mode) : + d_noverruns (0), + d_deci_rate (DEFAULT_DECIMATION_RATE), + d_rx_freq ((unsigned long) DEFAULT_RX_FREQ), + d_gain(DEFAULT_GAIN), + d_verbose (false), + d_updated(false), + d_msdd_command_type((msdd_command_type_t) opp_mode), + d_msdd_fft_mode(DEFAULT_FFT_MODE), + d_desired_sample_size(2^15), + d_fft_points (DEFAULT_FFT_POINTS) + { + } + + int d_noverruns; + msdd_decimation_t d_deci_rate; + unsigned long d_rx_freq; + double d_gain; + bool d_verbose; + bool d_updated; + msdd_command_type_t d_msdd_command_type; + msdd_fft_mode_t d_msdd_fft_mode; + unsigned long d_desired_sample_size; + + int d_socket; // handle to socket + int d_socket_rcv; // handle to socket retuned in the accept call + struct in_addr d_ip_src; // store the source IP address to use + unsigned short d_port_src; // the port number to open for connections to this service + sockaddr_in d_sockaddr_src; // store the source sockaddr data (formatted IP address and port number) + std::auto_ptr d_temp_buff; // hold buffer between calls + + omni_mutex d_mutex; + msdd_fft_points_t d_fft_points; + + struct msdd_request_fft_packet { + msdd_command_type_t command_type; + int foo_x20; + unsigned int center_freq_mhz; + int offset_freq_hz; + int gain; + msdd_fft_window_type_t window_type; + msdd_fft_points_t fft_points; + msdd_decimation_t decimation; + msdd_fft_mode_t fft_mode; + int number_sets; + } __attribute__((__packed__)); + + struct msdd_request_iq_packet { + msdd_command_type_t command_type; + int foo0x18; + unsigned int center_freq_mhz; + int offset_freq_hz; + int gain; + int number; + msdd_decimation_t decimation; + int number_sets; + } __attribute__((__packed__)); + + void make_request_fft_packet(msdd_request_fft_packet& packet); + + void make_request_iq_packet(msdd_request_iq_packet& packet, unsigned int number_samples); + + msdd_request_fft_packet d_fft_request_packet; // fft request packet + msdd_request_iq_packet d_iq_request_packet; // fft request packet +}; + + +msdd_source_base::msdd_source_base (const std::string &name, + gr_io_signature_sptr output_signature, + int which_board, + int opp_mode, + const char *src, + unsigned short port_src + ) throw (std::runtime_error) + : gr_sync_block (name, + gr_make_io_signature (0, 0, 0), + output_signature), + pimpl(new Impl(opp_mode)) +{ + int ret (0); + + // Set up the address stucture for the source address and port numbers + // Get the source IP address from the host name + struct hostent *hsrc (gethostbyname(src)); + + if(hsrc) { // if the source was provided as a host namex + pimpl->d_ip_src = *(struct in_addr*)hsrc->h_addr_list[0]; + } + else { // assume it was specified as an IP address + if((ret=inet_aton(src, &pimpl->d_ip_src)) == 0) { // format IP address + perror("Not a valid source IP address or host name"); + throw std::runtime_error("can't initialize source socket"); + } + } + + pimpl->d_port_src = htons(port_src); // format port number + + pimpl->d_sockaddr_src.sin_family = AF_INET; + pimpl->d_sockaddr_src.sin_addr = pimpl->d_ip_src; + pimpl->d_sockaddr_src.sin_port = pimpl->d_port_src; + + pimpl->d_temp_buff.reset(new unsigned char[OUTPUT_MAX + + std::max(sizeof(Impl::msdd_request_iq_packet), + sizeof(Impl::msdd_request_fft_packet))]); // allow it to hold up to payload_size bytes + + set_output_multiple (OUTPUT_MAX / output_signature->sizeof_stream_item (0)); +} + + +bool +msdd_source_base::open() +{ + omni_mutex_lock l(pimpl->d_mutex); // hold mutex for duration of this function + // create socket + MSDD_DEBUG2("MSDD: Before socket ") + pimpl->d_socket = socket(PF_INET, SOCK_STREAM, 0); + if(pimpl->d_socket == -1) { + perror("socket open"); + throw std::runtime_error("can't open socket"); + } + + // Turn on reuse address + int opt_val (1); + if(setsockopt(pimpl->d_socket, SOL_SOCKET, SO_REUSEADDR, (void*)&opt_val, sizeof(int)) == -1) { + perror("SO_REUSEADDR"); + throw std::runtime_error("can't set socket option SO_REUSEADDR"); + } + + // Don't wait when shutting down + linger lngr; + lngr.l_onoff = 1; + lngr.l_linger = 0; + if(setsockopt(pimpl->d_socket, SOL_SOCKET, SO_LINGER, (void*)&lngr, sizeof(linger)) == -1) { + perror("SO_LINGER"); + throw std::runtime_error("can't set socket option SO_LINGER"); + } + + // Set a timeout on the receive function to not block indefinitely + // This value can (and probably should) be changed + // timeval timeout; + // timeout.tv_sec = 1; + // timeout.tv_usec = 0; + // if(setsockopt(d_socket, SOL_SOCKET, SO_RCVTIMEO, (void*)&timeout, sizeof(timeout)) == -1) { + // perror("SO_RCVTIMEO"); + // throw std::runtime_error("can't set socket option SO_RCVTIMEO"); + // } + + // bind socket to an address and port number to listen on + MSDD_DEBUG2("MSDD: Before socket bind to " << pimpl->d_sockaddr_src.sin_port) + if(::connect(pimpl->d_socket, (struct sockaddr*)&pimpl->d_sockaddr_src, sizeof(pimpl->d_sockaddr_src)) == -1) { + perror("socket bind"); + throw std::runtime_error("can't bind socket"); + } + + MSDD_DEBUG2("MSDD: Socket open") + pimpl->d_updated = true; + return pimpl->d_socket != 0; +} + +/* read n bytes from a socket descriptor */ +int +msdd_source_base::readsock(int sockfd, unsigned char* buf, int nbytes) { + int nleft (nbytes); + int nread (0); + + while (nleft > 0) { + MSDD_DEBUG2("MSDD: Before socket read: " << nleft) + if ((nread = ::read(sockfd, buf, nleft)) < 0) { + return(nread); /* error, nread < 0 */ + } else if (nread == 0) { + break; + } + + nleft -= nread; + buf += nread; + } + return(nbytes - nleft); +} + +bool +msdd_source_base::close() +{ + omni_mutex_lock l(pimpl->d_mutex); // hold mutex for duration of this function + + if (pimpl->d_socket){ + shutdown(pimpl->d_socket, SHUT_RDWR); + pimpl->d_socket = 0; + } + pimpl->d_updated = true; + + return true; +} + +msdd_source_base::~msdd_source_base () +{ + msdd_source_base::close(); +} + +unsigned int +msdd_source_base::sizeof_basic_sample() const +{ + switch (pimpl->d_msdd_command_type) { + case SAMPLES_REALTIME: + return 4; + case SAMPLES_FFT: + switch (pimpl->d_msdd_fft_mode) { + case MODE_IQ: + case MODE_MAG: + return 4; + case MODE_MAGDB: + return 1; + default: + assert (false); // bad mode + } + default: + assert (false); // bad mode + } +} + +bool +msdd_source_base::start() +{ + return msdd_source_base::open(); +} + +bool +msdd_source_base::stop() +{ + return msdd_source_base::close(); +} + +void* +msdd_source_base::make_request_packet(unsigned int& size, unsigned int number_samples) { + switch (pimpl->d_msdd_command_type) { + case SAMPLES_REALTIME: + pimpl->make_request_iq_packet(pimpl->d_iq_request_packet, number_samples); + size = sizeof (pimpl->d_iq_request_packet); + return &pimpl->d_iq_request_packet; + case SAMPLES_FFT: + pimpl->make_request_fft_packet(pimpl->d_fft_request_packet); + size = sizeof (pimpl->d_fft_request_packet); + return &pimpl->d_fft_request_packet; + default: + assert (false); // bad mode + } +} + +void +msdd_source_base::Impl::make_request_fft_packet(msdd_request_fft_packet& packet) +{ + packet.command_type = SAMPLES_FFT; // FFT samples Command + packet.foo_x20 = 0x20; + packet.center_freq_mhz = d_rx_freq; + packet.offset_freq_hz = 0; + packet.gain = (int) d_gain; // gain + packet.window_type = WINDOW_HANNING; // magic number + packet.fft_points = d_fft_points; + packet.decimation = d_deci_rate; + packet.fft_mode = MODE_MAGDB; + packet.number_sets = 1; +} + +void +msdd_source_base::Impl::make_request_iq_packet(msdd_request_iq_packet& packet, unsigned int number_samples) +{ + packet.command_type = SAMPLES_REALTIME; // FFT samples Command + packet.foo0x18 = 0x18; // magic number + packet.center_freq_mhz = d_rx_freq; + packet.offset_freq_hz = 0; + packet.gain = (int) d_gain; // gain + packet.number = number_samples * 4; + packet.decimation = d_deci_rate; + packet.number_sets = 1; +} + +int +msdd_source_base::work (int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) +{ + int output_index (0); + int output_items_produced; + int bytes_read; + + unsigned int packet_size; + + MSDD_DEBUG("MSDD: requested items: " << noutput_items) + int noutput_items_desired = std::min (noutput_items, (int) pimpl->d_desired_sample_size); + MSDD_DEBUG("MSDD: desired items: " << noutput_items_desired) + + while (output_index < noutput_items_desired){ + + int nbytes = (pimpl->d_msdd_command_type == SAMPLES_REALTIME) ? + ninput_bytes_reqd_for_noutput_items (noutput_items_desired - output_index) : + ninput_bytes_reqd_for_noutput_items (msdd_source_base::fft_points()); + + void* request_packet = msdd_source_base::make_request_packet(packet_size, noutput_items_desired); + + nbytes = std::min (nbytes, OUTPUT_MAX); + MSDD_DEBUG2("MSDD: payload sizes: nbytes1: " << nbytes ) + + // send request + int result_nbytes = ::write(pimpl->d_socket, request_packet, packet_size); + //assert (result_nbytes == sizeof(msdd_request_packet)); + + // receive ack + result_nbytes = ::read (pimpl->d_socket, (unsigned char*) request_packet, packet_size); + MSDD_DEBUG2("MSDD: response: " << result_nbytes) + //assert (result_nbytes == sizeof(msdd_request_packet)); + + // receive payload + result_nbytes = msdd_source_base::readsock (pimpl->d_socket, pimpl->d_temp_buff.get(), nbytes); + MSDD_DEBUG("MSDD: reading bytes: " << nbytes << " received: " << result_nbytes) + if (result_nbytes > (int) nbytes){ + // fprintf (stderr, "msdd_source: overrun\n"); + fputs ("uO", stderr); + pimpl->d_noverruns++; + result_nbytes = nbytes; // truncate + } + + if (result_nbytes < 0) // We've got a problem. Usually board unplugged or powered down. + return -1; // Indicate we're done. + + if (result_nbytes != nbytes){ // not really an error, but unexpected + fprintf (stderr, "msdd_source: short read. Expected %d, got %d\n", + nbytes, result_nbytes); + } + + copy_from_msdd_buffer (output_items, + output_index, + noutput_items_desired - output_index, // output_items_available + output_items_produced, // [out] + pimpl->d_temp_buff.get(), // usrp_buffer + result_nbytes, + bytes_read); // [out] + + output_index += output_items_produced; + + if (pimpl->d_msdd_command_type == SAMPLES_FFT) break; + } + + MSDD_DEBUG("MSDD: items produced: " << output_items_produced << " index: " << output_index) + + //assert(false); + return output_index; +} + + +bool +msdd_source_base::set_decim_rate (unsigned int rate) +{ + bool result (true); + switch (rate) { + case 1: + pimpl->d_deci_rate = D0; + break; + case 2: + pimpl->d_deci_rate = D1; + break; + case 4: + pimpl->d_deci_rate = D2; + break; + case 8: + pimpl->d_deci_rate = D3; + break; + case 16: + pimpl->d_deci_rate = D4; + break; + case 32: + pimpl->d_deci_rate = D5; + break; + case 64: + pimpl->d_deci_rate = D6; + break; + case 128: + pimpl->d_deci_rate = D7; + break; + case 256: + pimpl->d_deci_rate = D8; + break; + default: + result = false; + } + + return result; +} +// +//bool +//msdd_source_base::set_nchannels (int nchan) +//{ +// // return d_usrp->set_nchannels (nchan); +// return true; +//} +// +//bool +//msdd_source_base::set_mux (int mux) +//{ +// return d_usrp->set_mux (mux); +//} + +bool +msdd_source_base::set_rx_freq (int channel, double freq) +{ + assert (channel == 0); + bool result (false); + + if (freq >= 30e6 && freq <= 6e9) { + pimpl->d_rx_freq = (unsigned long) freq / 1000000; + result = true; + } + + return result; +} + + +unsigned long +msdd_source_base::set_fft_size (int channel, unsigned long fft_size) +{ + assert (channel == 1); + + switch (fft_size) { + case 256: + pimpl->d_fft_points = S256; + break; + case 512: + pimpl->d_fft_points = S512; + break; + case 1024: + pimpl->d_fft_points = S1024; + break; + case 2048: + pimpl->d_fft_points = S2048; + break; + case 4096: + pimpl->d_fft_points = S4096; + break; + case 8192: + pimpl->d_fft_points = S8192; + break; + case 16384: + pimpl->d_fft_points = S16384; + break; + case 32768: + pimpl->d_fft_points = S32768; + break; + } + + return msdd_source_base::fft_points(); +} + +// +//long +//msdd_source_base::fpga_master_clock_freq() const +//{ +// return d_usrp->fpga_master_clock_freq(); +//} +// +//long +//msdd_source_base::converter_rate() const +//{ +// // return d_usrp->converter_rate(); +// return 8; +//} + +unsigned int +msdd_source_base::decim_rate () const +{ + return 1 << pimpl->d_deci_rate; +} +// +//int +//msdd_source_base::nchannels () const +//{ +// return d_usrp->nchannels (); +//} +// +//int +//msdd_source_base::mux () const +//{ +// return d_usrp->mux (); +//} + +double +msdd_source_base::rx_freq (int channel) const +{ + assert (channel == 0); + + return pimpl->d_rx_freq; +} + +unsigned int +msdd_source_base::fft_points() const +{ + return (1 << pimpl->d_fft_points); +} + +int +msdd_source_base::noverruns () const +{ + return pimpl->d_noverruns; +} + +//bool +//msdd_source_base::set_fpga_mode (int mode) +//{ +// return d_usrp->set_fpga_mode (mode); +//} +// +//bool +//msdd_source_base::set_ddc_phase (int channel, int phase) +//{ +// return d_usrp->set_ddc_phase(channel, phase); +//} +// +//bool +//msdd_source_base::set_dc_offset_cl_enable(int bits, int mask) +//{ +// return d_usrp->set_dc_offset_cl_enable(bits, mask); +//} + +void +msdd_source_base::set_verbose (bool verbose) +{ + pimpl->d_verbose = verbose; +} +// +//bool +//msdd_source_base::write_aux_dac (int which_dboard, int which_dac, int value) +//{ +// return d_usrp->write_aux_dac (which_dboard, which_dac, value); +//} +// +//int +//msdd_source_base::read_aux_adc (int which_dboard, int which_adc) +//{ +// return d_usrp->read_aux_adc (which_dboard, which_adc); +//} +// +//bool +//msdd_source_base::write_eeprom (int i2c_addr, int eeprom_offset, const std::string buf) +//{ +// return d_usrp->write_eeprom (i2c_addr, eeprom_offset, buf); +//} +// +//std::string +//msdd_source_base::read_eeprom (int i2c_addr, int eeprom_offset, int len) +//{ +// return d_usrp->read_eeprom (i2c_addr, eeprom_offset, len); +//} +// +//bool +//msdd_source_base::write_i2c (int i2c_addr, const std::string buf) +//{ +// return d_usrp->write_i2c (i2c_addr, buf); +//} +// +//std::string +//msdd_source_base::read_i2c (int i2c_addr, int len) +//{ +// return d_usrp->read_i2c (i2c_addr, len); +//} +// +bool +msdd_source_base::set_pga (int which, double gain) +{ + if (gain >= PGA_MIN & gain <= PGA_MAX) { + pimpl->d_gain = gain; + return true; + } + return false; +} + +double +msdd_source_base::pga (int which) const +{ + return pimpl->d_gain; +} + +double +msdd_source_base::pga_min () const +{ + return PGA_MIN; +} + +double +msdd_source_base::pga_max () const +{ + return PGA_MAX; +} + +double +msdd_source_base::pga_db_per_step () const +{ + return PGA_STEP; +} + +//int +//msdd_source_base::daughterboard_id (int which) const +//{ +// return d_usrp->daughterboard_id (which); +//} +// +// +//bool +//msdd_source_base::set_adc_offset (int which, int offset) +//{ +// return d_usrp->set_adc_offset (which, offset); +//} +// +//bool +//msdd_source_base::set_dac_offset (int which, int offset, int offset_pin) +//{ +// return d_usrp->set_dac_offset (which, offset, offset_pin); +//} +// +//bool +//msdd_source_base::set_adc_buffer_bypass (int which, bool bypass) +//{ +// return d_usrp->set_adc_buffer_bypass (which, bypass); +//} + +std::string +msdd_source_base::serial_number() +{ + return "SoftTronics MSDD 6000"; +} +// +//bool +//msdd_source_base::_write_oe (int which_dboard, int value, int mask) +//{ +// return d_usrp->_write_oe (which_dboard, value, mask); +//} +// +//bool +//msdd_source_base::write_io (int which_dboard, int value, int mask) +//{ +// return d_usrp->write_io (which_dboard, value, mask); +//} +// +//int +//msdd_source_base::read_io (int which_dboard) +//{ +// return d_usrp->read_io (which_dboard); +//} +// +// +// +// +//// internal routines... +// +//bool +//msdd_source_base::_write_fpga_reg (int regno, int value) +//{ +// return d_usrp->_write_fpga_reg (regno, value); +//} +// +//bool +//msdd_source_base::_write_fpga_reg_masked (int regno, int value, int mask) +//{ +// return d_usrp->_write_fpga_reg_masked (regno, value, mask); +//} +// +//int +//msdd_source_base::_read_fpga_reg (int regno) +//{ +// return d_usrp->_read_fpga_reg (regno); +//} +// +//bool +//msdd_source_base::_write_9862 (int which_codec, int regno, unsigned char value) +//{ +// return d_usrp->_write_9862 (which_codec, regno, value); +//} +// +//int +//msdd_source_base::_read_9862 (int which_codec, int regno) const +//{ +// return d_usrp->_read_9862 (which_codec, regno); +//} +// +//bool +//msdd_source_base::_write_spi (int optional_header, int enables, +// int format, std::string buf) +//{ +// return d_usrp->_write_spi (optional_header, enables, format, buf); +//} +// +//std::string +//msdd_source_base::_read_spi (int optional_header, int enables, int format, int len) +//{ +// return d_usrp->_read_spi (optional_header, enables, format, len); +//} +// +//bool +//msdd_source_base::set_format(unsigned int format) +//{ +// return d_usrp->set_format(format); +//} +// +//unsigned int +//msdd_source_base::format() const +//{ +// return d_usrp->format(); +//} +// +//unsigned int +//msdd_source_base::make_format(int width, int shift, bool want_q, bool bypass_halfband) +//{ +// return usrp_standard_rx::make_format(width, shift, want_q, bypass_halfband); +//} +// +//int +//msdd_source_base::format_width(unsigned int format) +//{ +// return usrp_standard_rx::format_width(format); +//} +// +//int +//msdd_source_base::format_shift(unsigned int format) +//{ +// return usrp_standard_rx::format_shift(format); +//} +// +//bool +//msdd_source_base::format_want_q(unsigned int format) +//{ +// return usrp_standard_rx::format_want_q(format); +//} +// +//bool +//msdd_source_base::format_bypass_halfband(unsigned int format) +//{ +// return usrp_standard_rx::format_bypass_halfband(format); +//} + +bool msdd_source_base::set_desired_packet_size (int which, unsigned long packet_size) { + bool result(false); + + if (pimpl->d_desired_sample_size < 2^32) { // FIXME: find maximum sample request for MSDD check if greater than + pimpl->d_desired_sample_size = packet_size; + } + return result; +} + +unsigned long msdd_source_base::desired_packet_size (int which) const { + return pimpl->d_desired_sample_size; +} diff --git a/gr-msdd6000/src/lib/msdd_source_base.h b/gr-msdd6000/src/lib/msdd_source_base.h new file mode 100644 index 00000000..4a5bde15 --- /dev/null +++ b/gr-msdd6000/src/lib/msdd_source_base.h @@ -0,0 +1,307 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 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. + */ + +#ifndef INCLUDED_MSDD_SOURCE_BASE_H +#define INCLUDED_MSDD_SOURCE_BASE_H +#include +#include + +/*! + * \brief abstract interface to MSDD 6000 Softronics module Rx path (Rev 1) + */ + +class msdd_source_base : public gr_sync_block { +public: + enum msdd_command_type_t { + COMMAND_STATUS = 0, + SAMPLES_REALTIME = 1, + SAMPLES_FFT = 2, + }; + + enum msdd_fft_window_type_t { + WINDOW_RECTANGLE = 0, + WINDOW_HANNING = 1, + WINDOW_HAMMING = 2, + WINDOW_BLACKMAN = 3 + }; + + enum msdd_fft_mode_t { + MODE_IQ=0, + MODE_MAG=1, + MODE_MAGDB=2 + }; + + enum msdd_decimation_t { + D0=0, + D1=1, + D2=2, + D3=3, + D4=4, + D5=5, + D6=6, + D7=7, + D8=8 + }; + + enum msdd_fft_points_t { + S256=8, + S512=9, + S1024=10, + S2048=11, + S4096=12, + S8192=13, + S16384=14, + S32768=15 + }; + +private: + + class Impl; + friend class Impl; + std::auto_ptr pimpl; + +protected: + + msdd_source_base (const std::string &name, + gr_io_signature_sptr output_signature, + int which_board, + int opp_mode, + const char *src, + unsigned short port_src + ) throw (std::runtime_error); + + /*! + * \brief return number of msdd input bytes required to produce noutput items. + */ + virtual int ninput_bytes_reqd_for_noutput_items (int noutput_items) = 0; + + /*! + * \brief number of bytes in a low-level sample + */ + unsigned int sizeof_basic_sample() const; + + virtual void copy_from_msdd_buffer (gr_vector_void_star &output_items, + int output_index, + int output_items_available, + int &output_items_produced, + const void *msdd_buffer, + int msdd_buffer_length, + int &bytes_read) = 0; + + int readsock(int sockfd, unsigned char* buf, int nbytes); + + void* make_request_packet(unsigned int& size, unsigned int number_samples); + + unsigned long set_fft_size (int channel, unsigned long fft_size); + +public: + //! magic value used on alternate register read interfaces + static const int READ_FAILED = -99999; + + ~msdd_source_base (); + + int work (int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + + bool start(); + bool stop(); + + /*! + * \brief open a socket specified by the port and ip address info + * + * Opens a socket, binds to the address, and waits for a connection + * over UDP. If any of these fail, the fuction retuns the error and exits. + */ + bool open(); + + /*! + * \brief Close current socket. + * + * Shuts down read/write on the socket + */ + bool close(); + + /*! + * \brief Set decimator rate. \p rate must be EVEN and in [8, 256]. + * + * The final complex sample rate across the USB is + * adc_freq () / decim_rate () + */ + bool set_decim_rate (unsigned int rate); + //bool set_nchannels (int nchan); + //bool set_mux (int mux); + + /*! + * \brief set the center frequency of the digital down converter. + * + * \p channel must be 0. \p freq is the center frequency in Hz. + * It must be in the range [-FIXME, FIXME]. The frequency specified is + * quantized. Use rx_freq to retrieve the actual value used. + */ + bool set_rx_freq (int channel, double freq); + + /*! + * \brief + */ + bool set_opp_mode (int channel, msdd_command_type_t mode); + +// +// /*! +// * \brief set fpga special modes +// */ +// bool set_fpga_mode (int mode); + + void set_verbose (bool verbose); +// +// /*! +// * \brief Set the digital down converter phase register. +// * +// * \param channel which ddc channel [0, 3] +// * \param phase 32-bit integer phase value. +// */ +// bool set_ddc_phase(int channel, int phase); +// + /*! + * \brief Set Programmable Gain Amplifier (PGA) + * + * \param which which A/D [0,3] + * \param gain_in_db gain value (linear in dB) + * + * gain is rounded to closest setting supported by hardware. + * + * \returns true iff sucessful. + * + * \sa pga_min(), pga_max(), pga_db_per_step() + */ + bool set_pga (int which, double gain_in_db); + + /*! + * \brief Return programmable gain amplifier gain setting in dB. + * + * \param which which A/D [0,3] + */ + double pga (int which) const; + + /*! + * \brief Return minimum legal PGA setting in dB. + */ + double pga_min () const; + + /*! + * \brief Return maximum legal PGA setting in dB. + */ + double pga_max () const; + + /*! + * \brief Return hardware step size of PGA (linear in dB). + */ + double pga_db_per_step () const; + + // ACCESSORS + +// long converter_rate() const; + + unsigned int decim_rate () const; +// int nchannels () const; +// int mux () const; + double rx_freq (int channel) const; + unsigned int fft_points() const; + int noverruns () const; + + /*! + * \brief return the msdd's serial number. + * + * \returns non-zero length string iff successful. + */ + std::string serial_number(); + +// /*! +// * \brief Enable/disable automatic DC offset removal control loop in FPGA +// * +// * \param bits which control loops to enable +// * \param mask which \p bits to pay attention to +// * +// * If the corresponding bit is set, enable the automatic DC +// * offset correction control loop. +// * +// *
+//   * The 4 low bits are significant:
+//   *
+//   *   ADC0 = (1 << 0)
+//   *   ADC1 = (1 << 1)
+//   *   ADC2 = (1 << 2)
+//   *   ADC3 = (1 << 3)
+//   * 
+// * +// * By default the control loop is enabled on all ADC's. +// */ +// bool set_dc_offset_cl_enable(int bits, int mask); + + /*! + * \brief Specify Rx data format. + * + * \param format format specifier + * + * Rx data format control register + * + * 3 2 1 + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +-----------------------------------------+-+-+---------+-------+ + * | Reserved (Must be zero) |B|Q| WIDTH | SHIFT | + * +-----------------------------------------+-+-+---------+-------+ + * + * SHIFT specifies arithmetic right shift [0, 15] + * WIDTH specifies bit-width of I & Q samples across the USB [1, 16] (not all valid) + * Q if set deliver both I & Q, else just I + * B if set bypass half-band filter. + * + * Right now the acceptable values are: + * + * B Q WIDTH SHIFT + * 0 1 16 0 + * 0 1 8 8 + * + * More valid combos to come. + * + * Default value is 0x00000300 16-bits, 0 shift, deliver both I & Q. + */ +// bool set_format(unsigned int format); + + /*! + * \brief return current format + */ +// unsigned int format () const; +// +// static unsigned int make_format(int width=16, int shift=0, +// bool want_q=true, bool bypass_halfband=false); +// static int format_width(unsigned int format); +// static int format_shift(unsigned int format); +// static bool format_want_q(unsigned int format); +// static bool format_bypass_halfband(unsigned int format); + + bool set_desired_packet_size (int which, unsigned long packet_size); + + unsigned long desired_packet_size (int which) const; +}; + +#endif /* INCLUDED_MSDD_SOURCE_BASE_H */ diff --git a/gr-msdd6000/src/lib/msdd_source_c.cc b/gr-msdd6000/src/lib/msdd_source_c.cc new file mode 100644 index 00000000..454d9d41 --- /dev/null +++ b/gr-msdd6000/src/lib/msdd_source_c.cc @@ -0,0 +1,111 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +//#define MSDD_DEBUG2_TRUE + +#ifdef MSDD_DEBUG2_TRUE +#include +#define MSDD_DEBUG2(x) std::cout << x << std::endl; +#else +#define MSDD_DEBUG2(x) +#endif + +#include +#include + +namespace { + static const int NBASIC_SAMPLES_PER_ITEM = 2; // I & Q +}; + +msdd_source_c_sptr +msdd_make_source_c (int which_board, + int opp_mode, + const char *src, + unsigned short port_src + ) throw (std::runtime_error) +{ + return msdd_source_c_sptr (new msdd_source_c ( + which_board, + opp_mode, + src, + port_src + )); +} + +msdd_source_c::msdd_source_c (int which_board, + int opp_mode, + const char *src, + unsigned short port_src + ) throw (std::runtime_error) + : msdd_source_base ("msdd_source_c", + gr_make_io_signature (1, 1, 2 * sizeof (int)), + which_board, opp_mode, src, port_src + ) +{ + + switch (sizeof_basic_sample()) { + case 4: + d_buffer_copy_behavior.reset( + new msdd::BufferCopyBehaviorComplex ()); + break; + default: + assert(false); + } + +} + +msdd_source_c::~msdd_source_c () +{ +} + +int +msdd_source_c::ninput_bytes_reqd_for_noutput_items (int noutput_items) +{ + return noutput_items * NBASIC_SAMPLES_PER_ITEM * sizeof_basic_sample(); +} + +/* + * Copy 8 bit fft from mdss buffer into output buffer + */ +void +msdd_source_c::copy_from_msdd_buffer (gr_vector_void_star &output_items, + int output_index, + int output_items_available, + int &output_items_produced, + const void *msdd_buffer, + int buffer_length, + int &bytes_read) +{ + unsigned nmsdd_bytes_per_item = NBASIC_SAMPLES_PER_ITEM * sizeof_basic_sample(); + + unsigned int nitems = std::min (output_items_available, + (int)(buffer_length / nmsdd_bytes_per_item)); + + (*d_buffer_copy_behavior.get())(output_items, msdd_buffer, output_index, nitems); + + output_items_produced = nitems; + bytes_read = nitems * nmsdd_bytes_per_item; +} diff --git a/gr-msdd6000/src/lib/msdd_source_c.h b/gr-msdd6000/src/lib/msdd_source_c.h new file mode 100644 index 00000000..11aafbb1 --- /dev/null +++ b/gr-msdd6000/src/lib/msdd_source_c.h @@ -0,0 +1,79 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 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. + */ + +#ifndef INCLUDED_MSDD_SOURCE_C_H +#define INCLUDED_MSDD_SOURCE_C_H + +#include +#include +#include + +class msdd_source_c; +typedef boost::shared_ptr msdd_source_c_sptr; + +// public shared_ptr constructor + +msdd_source_c_sptr +msdd_make_source_c (int which_board, + int opp_mode, + const char *src, + unsigned short port_src + ) throw (std::runtime_error); + +/*! + * \brief interface to MSDD Rx path + * + * output: 1 stream of short + */ +class msdd_source_c : public msdd_source_base { +private: + friend msdd_source_c_sptr + msdd_make_source_c (int which_board, + int opp_mode, + const char *src, + unsigned short port_src + ) throw (std::runtime_error); + + std::auto_ptr d_buffer_copy_behavior; + +protected: + msdd_source_c (int which_board, + int opp_mode, + const char *src, + unsigned short port_src + ) throw (std::runtime_error); + + int ninput_bytes_reqd_for_noutput_items (int noutput_items); + + virtual void copy_from_msdd_buffer (gr_vector_void_star &output_items, + int output_index, + int output_items_available, + int &output_items_produced, + const void *msdd_buffer, + int buffer_length, + int &bytes_read); + +public: + ~msdd_source_c (); +}; + +#endif /* INCLUDED_MSDD_SOURCE_C_H */ diff --git a/gr-msdd6000/src/lib/msdd_source_s.cc b/gr-msdd6000/src/lib/msdd_source_s.cc new file mode 100644 index 00000000..6a218080 --- /dev/null +++ b/gr-msdd6000/src/lib/msdd_source_s.cc @@ -0,0 +1,130 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#define MSDD_DEBUG2_TRUE + +#ifdef MSDD_DEBUG2_TRUE +#include +#define MSDD_DEBUG2(x) std::cout << x << std::endl; +#else +#define MSDD_DEBUG2(x) +#endif + +#include +#include + +namespace { + static const int NBASIC_SAMPLES_PER_ITEM = 1; +} + +msdd_source_s_sptr +msdd_make_source_s (int which_board, + unsigned int decim_rate, + unsigned int fft_points, + double initial_rx_freq, + int opp_mode, + const char *src, + unsigned short port_src + ) throw (std::runtime_error) +{ + return msdd_source_s_sptr (new msdd_source_s (which_board, + decim_rate, + fft_points, + initial_rx_freq, + opp_mode, + src, + port_src + )); +} + + +msdd_source_s::msdd_source_s (int which_board, + unsigned int decim_rate, + unsigned int fft_points, + double initial_rx_freq, + int opp_mode, + const char *src, + unsigned short port_src + ) throw (std::runtime_error) + : msdd_source_base ("msdd_source_s", + gr_make_io_signature (1, 1, sizeof (int)), + which_board, opp_mode, src, port_src + ) +{ + + switch (sizeof_basic_sample()) { + case 1: + d_buffer_copy_behavior.reset( + new msdd::BufferCopyBehaviorGeneric()); + break; + case 2: + d_buffer_copy_behavior.reset( + new msdd::BufferCopyBehaviorGeneric()); + break; + case 4: + d_buffer_copy_behavior.reset( + new msdd::BufferCopyBehaviorGeneric()); + break; + default: + assert(false); + } + +} + +msdd_source_s::~msdd_source_s () +{ +} + +int +msdd_source_s::ninput_bytes_reqd_for_noutput_items (int noutput_items) +{ + return noutput_items * NBASIC_SAMPLES_PER_ITEM * sizeof_basic_sample(); +} + +void +msdd_source_s::copy_from_msdd_buffer (gr_vector_void_star &output_items, + int output_index, + int output_items_available, + int &output_items_produced, + const void *msdd_buffer, + int buffer_length, + int &bytes_read) +{ + MSDD_DEBUG2("copy_from_msdd_buffer: output_index: " << output_index << " output_items_available: " << output_items_available << " buflen: " << buffer_length) + + unsigned nmsdd_bytes_per_item + (msdd_source_s::ninput_bytes_reqd_for_noutput_items(1)); + + unsigned int nitems = std::min (output_items_available, + (int)(buffer_length / nmsdd_bytes_per_item)); + + MSDD_DEBUG2("copy_from_msdd_buffer: nmsdd_bytes_per_item: " << nmsdd_bytes_per_item << " nitems: " << nitems) + + (*d_buffer_copy_behavior.get())(output_items, msdd_buffer, output_index, nitems); + + output_items_produced = nitems; + bytes_read = nitems * nmsdd_bytes_per_item; +} diff --git a/gr-msdd6000/src/lib/msdd_source_s.h b/gr-msdd6000/src/lib/msdd_source_s.h new file mode 100644 index 00000000..e32f8ae9 --- /dev/null +++ b/gr-msdd6000/src/lib/msdd_source_s.h @@ -0,0 +1,87 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 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. + */ + +#ifndef INCLUDED_MSDD_SOURCE_S_H +#define INCLUDED_MSDD_SOURCE_S_H + +#include +#include +#include + +class msdd_source_s; +typedef boost::shared_ptr msdd_source_s_sptr; + +// public shared_ptr constructor + +msdd_source_s_sptr +msdd_make_source_s (int which_board, + unsigned int decim_rate, + unsigned int fft_points, + double initial_rx_freq, + int opp_mode, + const char *src, + unsigned short port_src + ) throw (std::runtime_error); + +/*! + * \brief interface to MSDD Rx path + * + * output: 1 stream of short + */ +class msdd_source_s : public msdd_source_base { +private: + friend msdd_source_s_sptr + msdd_make_source_s (int which_board, + unsigned int decim_rate, + unsigned int fft_points, + double initial_rx_freq, + int opp_mode, + const char *src, + unsigned short port_src + ) throw (std::runtime_error); + + std::auto_ptr d_buffer_copy_behavior; + +protected: + msdd_source_s (int which_board, + unsigned int decim_rate, + unsigned int fft_points, + double initial_rx_freq, + int opp_mode, + const char *src, + unsigned short port_src + ) throw (std::runtime_error); + + int ninput_bytes_reqd_for_noutput_items (int noutput_items); + + virtual void copy_from_msdd_buffer (gr_vector_void_star &output_items, + int output_index, + int output_items_available, + int &output_items_produced, + const void *msdd_buffer, + int buffer_length, + int &bytes_read); +public: + ~msdd_source_s (); +}; + +#endif /* INCLUDED_MSDD_SOURCE_S_H */ diff --git a/gr-msdd6000/src/python/Makefile.am b/gr-msdd6000/src/python/Makefile.am new file mode 100644 index 00000000..38040eb3 --- /dev/null +++ b/gr-msdd6000/src/python/Makefile.am @@ -0,0 +1,48 @@ +# +# Copyright 2008 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 + +EXAMPLE_FILES = + +EXTRA_DIST = run_tests.in \ + $(EXAMPLE_FILES) + +ourdatadir = $(exampledir)/msdd6000 +ourdata_DATA = $(EXAMPLE_FILES) + +noinst_PYTHON = \ + qa_msdd6000.py \ + test_udp.py \ + test_tcp.py + +TESTS = \ + run_tests + + +# Make example scripts with #! executable +install-data-local: install-ourdataDATA + for i in `find $(ourdatadir) -type f ! -perm 755`; do \ + if head -1 $$i | grep -q '^#!'; then \ + chmod 755 $$i; \ + echo "made executable: $$i"; \ + fi; \ + done diff --git a/gr-msdd6000/src/python/qa_msdd6000.py b/gr-msdd6000/src/python/qa_msdd6000.py new file mode 100755 index 00000000..fdcf7e83 --- /dev/null +++ b/gr-msdd6000/src/python/qa_msdd6000.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python +# +# Copyright 2008 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 msdd + +class qa_usrp (gr_unittest.TestCase): + + def setUp (self): + self.tb = gr.top_block () + + def tearDown (self): + self.tb = None + + def test_000_nop (self): + """Just see if we can import the module... + """ + pass + +if __name__ == '__main__': + gr_unittest.main () diff --git a/gr-msdd6000/src/python/run_tests.in b/gr-msdd6000/src/python/run_tests.in new file mode 100644 index 00000000..28e4c731 --- /dev/null +++ b/gr-msdd6000/src/python/run_tests.in @@ -0,0 +1,10 @@ +#!/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-msdd6000 \ + @abs_top_builddir@/gr-msdd6000 \ + @srcdir@ diff --git a/gr-msdd6000/src/python/test_tcp.py b/gr-msdd6000/src/python/test_tcp.py new file mode 100755 index 00000000..b02db815 --- /dev/null +++ b/gr-msdd6000/src/python/test_tcp.py @@ -0,0 +1,78 @@ +#!/usr/bin/python + +from socket import * +import string +import time +import struct; +import random; + +myport = random.randint(1025,65535); + +port = 10000 +host = "10.45.4.43" +myaddr = ("10.45.1.229",myport); + +buf = 100000; + +TCPSock = socket(AF_INET,SOCK_STREAM); +#TCPSock = socket(AF_INET,SOCK_DGRAM); +TCPSock.bind(myaddr); +TCPSock.connect((host,port)); + +f_mhz = 2400; +f_hz = 0; +gain = 2; +window = 3; #0=rect, 1=hanning, 2=hamming, 3=blackman +#samples = 0xffffffff; #8-15 fft:(returns 2^number[8-15]) raw:(returns number) +samples = 2; #8-15 fft:(returns 2^number[8-15]) raw:(returns number) +decim = 2; #0-8 +#decim = decim+16; # +16 to use 16bit instead of 32 bit +mode = 1; #0=IQ, 1=MAG, 2=MAGDB +sets = 0xffffffff; +#sets = 1; + +fft_data = struct.pack(" 1): + sets = 3; + raw_data_2 = struct.pack(" 1): + sets = 3; + raw_data_2 = struct.pack("