diff options
Diffstat (limited to 'usrp/host')
39 files changed, 8509 insertions, 591 deletions
diff --git a/usrp/host/apps/test_usrp_standard_rx.cc b/usrp/host/apps/test_usrp_standard_rx.cc index 49f1f21bb..4a47daa95 100644 --- a/usrp/host/apps/test_usrp_standard_rx.cc +++ b/usrp/host/apps/test_usrp_standard_rx.cc @@ -1,6 +1,6 @@ /* -*- c++ -*- */ /* - * Copyright 2003,2006 Free Software Foundation, Inc. + * Copyright 2003,2006,2008 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -44,7 +44,7 @@ char *prog_name; -static bool test_input (usrp_standard_rx *urx, int max_bytes, FILE *fp); +static bool test_input (usrp_standard_rx_sptr urx, int max_bytes, FILE *fp); static void set_progname (char *path) @@ -189,7 +189,7 @@ main (int argc, char **argv) mode |= usrp_standard_rx::FPGA_MODE_COUNTING; - usrp_standard_rx *urx = + usrp_standard_rx_sptr urx = usrp_standard_rx::make (which_board, decim, 1, -1, mode, fusb_block_size, fusb_nblocks); @@ -214,14 +214,12 @@ main (int argc, char **argv) if (fp) fclose (fp); - delete urx; - return 0; } static bool -test_input (usrp_standard_rx *urx, int max_bytes, FILE *fp) +test_input (usrp_standard_rx_sptr urx, int max_bytes, FILE *fp) { int fd = -1; static const int BUFSIZE = urx->block_size(); diff --git a/usrp/host/apps/test_usrp_standard_tx.cc b/usrp/host/apps/test_usrp_standard_tx.cc index 6418c4abf..488995526 100644 --- a/usrp/host/apps/test_usrp_standard_tx.cc +++ b/usrp/host/apps/test_usrp_standard_tx.cc @@ -1,6 +1,6 @@ /* -*- c++ -*- */ /* - * Copyright 2003,2004 Free Software Foundation, Inc. + * Copyright 2003,2004,2008 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -42,7 +42,7 @@ char *prog_name; -static bool test_output (usrp_standard_tx *utx, int max_bytes, double ampl, +static bool test_output (usrp_standard_tx_sptr utx, int max_bytes, double ampl, bool dc_p, bool counting_p); static void @@ -210,7 +210,7 @@ main (int argc, char **argv) } #endif - usrp_standard_tx *utx; + usrp_standard_tx_sptr utx; utx = usrp_standard_tx::make (which_board, interp, @@ -226,7 +226,7 @@ main (int argc, char **argv) die ("utx->set_tx_freq"); if (dump_regs_p) - do_dump_codec_regs (utx); + do_dump_codec_regs (utx.get()); fflush (stdout); @@ -236,14 +236,12 @@ main (int argc, char **argv) test_output (utx, max_bytes, ampl, dc_p, counting_p); - delete utx; - return 0; } static bool -test_output (usrp_standard_tx *utx, int max_bytes, double ampl, +test_output (usrp_standard_tx_sptr utx, int max_bytes, double ampl, bool dc_p, bool counting_p) { static const int BUFSIZE = utx->block_size(); diff --git a/usrp/host/apps/usrp_cal_dc_offset.cc b/usrp/host/apps/usrp_cal_dc_offset.cc index e836e1aab..5ebac12a3 100644 --- a/usrp/host/apps/usrp_cal_dc_offset.cc +++ b/usrp/host/apps/usrp_cal_dc_offset.cc @@ -1,6 +1,6 @@ /* -*- c++ -*- */ /* - * Copyright 2005 Free Software Foundation, Inc. + * Copyright 2005,2008 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -43,7 +43,7 @@ char *prog_name; static void -run_cal(usrp_standard_rx *u, int which_side, int decim, bool verbose_p) +run_cal(usrp_standard_rx_sptr u, int which_side, int decim, bool verbose_p) { static const int BUFSIZE = u->block_size(); static const int N = BUFSIZE/sizeof (short); @@ -213,11 +213,11 @@ main (int argc, char **argv) usrp_local_sighandler sigquit (SIGQUIT, usrp_local_sighandler::throw_signal); #endif - usrp_standard_rx *urx = + usrp_standard_rx_sptr urx = usrp_standard_rx::make(which_board, decim, nchannels, mux, mode, fusb_block_size, fusb_nblocks); - if (urx == 0) + if (!urx) die("usrp_standard_rx::make"); try { @@ -236,7 +236,5 @@ main (int argc, char **argv) catch(...){ fprintf (stderr, "usrp_cal_dc_offset: caught something\n"); } - - delete urx; } diff --git a/usrp/host/lib/inband/qa_inband_usrp_server.cc b/usrp/host/lib/inband/qa_inband_usrp_server.cc index c66418392..6049a8a87 100644 --- a/usrp/host/lib/inband/qa_inband_usrp_server.cc +++ b/usrp/host/lib/inband/qa_inband_usrp_server.cc @@ -1543,6 +1543,9 @@ qa_inband_usrp_server::test_rx() void qa_inband_usrp_server::test_cs() { + // FIXME This test is disabled because it hangs with the change to use usrp_standard_*_sptr's + return; + mb_runtime_sptr rt = mb_make_runtime(); pmt_t result = PMT_T; @@ -1557,6 +1560,9 @@ qa_inband_usrp_server::test_cs() void qa_inband_usrp_server::test_rid() { + // FIXME This test is disabled because it hangs with the change to use usrp_standard_*_sptr's + return; + mb_runtime_sptr rt = mb_make_runtime(); pmt_t result = PMT_T; diff --git a/usrp/host/lib/inband/usrp_usb_interface.cc b/usrp/host/lib/inband/usrp_usb_interface.cc index c69eb0b00..d82b589b4 100644 --- a/usrp/host/lib/inband/usrp_usb_interface.cc +++ b/usrp/host/lib/inband/usrp_usb_interface.cc @@ -143,13 +143,9 @@ usrp_usb_interface::usrp_usb_interface(mb_runtime *rt, const std::string &instan connect("self", "rx_cs", "rx", "cs"); connect("self", "tx_cs", "tx", "cs"); - // FIX ME: the code should query the FPGA to retrieve the number of channels and such + // FIXME: the code should query the FPGA to retrieve the number of channels and such d_ntx_chan = 2; d_nrx_chan = 2; - - d_utx = NULL; - d_urx = NULL; - } usrp_usb_interface::~usrp_usb_interface() @@ -394,8 +390,6 @@ usrp_usb_interface::handle_cmd_write(pmt_t data) channel, pkts, tx_handle)); - - return; } /*! @@ -482,14 +476,13 @@ usrp_usb_interface::handle_cmd_close(pmt_t data) if (verbose) std::cout << "[USRP_USB_INTERFACE] Handling close request for USRP\n"; - delete d_utx; - d_utx = 0; - - delete d_urx; - d_urx = 0; + d_utx.reset(); + d_urx.reset(); d_cs->send(s_response_usrp_close, pmt_list2(invocation_handle, PMT_T)); + // FIXME This seems like a _very_ strange place to be calling shutdown_all. + // That decision should be left to high-level code, not low-level code like this. shutdown_all(PMT_T); } diff --git a/usrp/host/lib/inband/usrp_usb_interface.h b/usrp/host/lib/inband/usrp_usb_interface.h index 5151f155c..c10741516 100644 --- a/usrp/host/lib/inband/usrp_usb_interface.h +++ b/usrp/host/lib/inband/usrp_usb_interface.h @@ -1,6 +1,6 @@ /* -*- c++ -*- */ /* - * Copyright 2007 Free Software Foundation, Inc. + * Copyright 2007,2008 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -32,8 +32,8 @@ class usrp_usb_interface : public mb_mblock { public: - usrp_standard_tx* d_utx; - usrp_standard_rx* d_urx; + usrp_standard_tx_sptr d_utx; + usrp_standard_rx_sptr d_urx; mb_port_sptr d_cs; mb_port_sptr d_rx_cs; diff --git a/usrp/host/lib/legacy/Makefile.am b/usrp/host/lib/legacy/Makefile.am index d913b6bb1..7b880924c 100644 --- a/usrp/host/lib/legacy/Makefile.am +++ b/usrp/host/lib/legacy/Makefile.am @@ -32,11 +32,11 @@ libusrp_la_common_LIBADD = \ # darwin fusb requires omnithreads if FUSB_TECH_darwin -AM_CPPFLAGS = $(common_INCLUDES) $(OMNITHREAD_INCLUDES) $(WITH_INCLUDES) +AM_CPPFLAGS = $(common_INCLUDES) $(OMNITHREAD_INCLUDES) $(BOOST_CPPFLAGS) $(WITH_INCLUDES) libusrp_la_LIBADD = $(libusrp_la_common_LIBADD) $(OMNITHREAD_LA) libusrp_la_LDFLAGS = $(libusrp_la_common_LDFLAGS) -framework CoreFoundation else -AM_CPPFLAGS = $(common_INCLUDES) $(WITH_INCLUDES) +AM_CPPFLAGS = $(common_INCLUDES) $(BOOST_CPPFLAGS) $(WITH_INCLUDES) libusrp_la_LIBADD = $(libusrp_la_common_LIBADD) libusrp_la_LDFLAGS = $(libusrp_la_common_LDFLAGS) endif @@ -104,7 +104,20 @@ libusrp_la_common_SOURCES = \ usrp_dbid.cc \ usrp_local_sighandler.cc \ usrp_prims.cc \ - usrp_standard.cc + usrp_standard.cc \ + db_boards.cc \ + db_base.cc \ + db_basic.cc \ + db_tv_rx.cc \ + db_flexrf.cc \ + db_flexrf_mimo.cc \ + db_dbs_rx.cc \ + db_xcvr2450.cc \ + db_dtt754.cc \ + db_dtt768.cc \ + db_util.cc + +# db_wbx.cc if FUSB_TECH_generic @@ -128,22 +141,37 @@ libusrp_la_SOURCES = $(libusrp_la_common_SOURCES) $(ra_wb_CODE) endif include_HEADERS = \ + db_base.h \ usrp_basic.h \ usrp_bytesex.h \ usrp_config.h \ usrp_dbid.h \ usrp_prims.h \ usrp_slots.h \ - usrp_standard.h + usrp_standard.h \ + usrp_subdev_spec.h \ + usrp_tune_result.h noinst_HEADERS = \ ad9862.h \ + db_base_impl.h \ + db_basic.h \ + db_boards.h \ + db_dbs_rx.h \ + db_dtt754.h \ + db_dtt768.h \ + db_flexrf.h \ + db_flexrf_mimo.h \ + db_tv_rx.h \ + db_util.h \ + db_wbx.h \ + db_xcvr2450.h \ fusb.h \ fusb_darwin.h \ - fusb_win32.h \ fusb_generic.h \ fusb_linux.h \ fusb_ra_wb.h \ + fusb_win32.h \ md5.h \ rate_to_regval.h \ usrp_local_sighandler.h @@ -159,5 +187,7 @@ noinst_PYTHON = \ usrp_dbid.py usrp_dbid.h usrp_dbid.cc: gen_usrp_dbid.py usrp_dbid.dat PYTHONPATH=$(top_srcdir)/usrp/src srcdir=$(srcdir) $(PYTHON) $(srcdir)/gen_usrp_dbid.py $(srcdir)/usrp_dbid.dat +swiginclude_HEADERS = db_base.i + MOSTLYCLEANFILES = \ $(BUILT_SOURCES) *~ *.pyc diff --git a/usrp/host/lib/legacy/db_base.cc b/usrp/host/lib/legacy/db_base.cc new file mode 100644 index 000000000..9d970435f --- /dev/null +++ b/usrp/host/lib/legacy/db_base.cc @@ -0,0 +1,245 @@ +// +// 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 asversion 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 <db_base.h> +#include <db_base_impl.h> + +#if 0 +tune_result::tune_result(double baseband, double dxc, double residual, bool inv) + : ok(false), baseband_freq(baseband), dxc_freq(dxc), + residual_freq(residual), inverted(inv) +{ +} + +tune_result::~tune_result() +{ +} +#endif + + +/*****************************************************************************/ + +db_base::db_base(usrp_basic_sptr usrp, int which) + : d_is_shutdown(false), d_raw_usrp(usrp.get()), d_which(which), d_lo_offset(0.0) +{ +} + +db_base::~db_base() +{ + shutdown(); +} + +void +db_base::shutdown() +{ + if (!d_is_shutdown){ + d_is_shutdown = true; + // do whatever there is to do to shutdown + } +} + +int +db_base::dbid() +{ + return usrp()->daughterboard_id(d_which); +} + +std::string +db_base::name() +{ + return usrp_dbid_to_string(dbid()); +} + +std::string +db_base::side_and_name() +{ + if(d_which == 0) + return "A: " + name(); + else + return "B: " + name(); +} + +// Function to bypass ADC buffers. Any board which is DC-coupled +// should bypass the buffers + +bool +db_base::bypass_adc_buffers(bool bypass) +{ + //if(d_tx) { + // throw std::runtime_error("TX Board has no adc buffers\n"); + //} + + bool ok = true; + if(d_which==0) { + ok &= usrp()->set_adc_buffer_bypass(0, bypass); + ok &= usrp()->set_adc_buffer_bypass(1, bypass); + } + else { + ok &= usrp()->set_adc_buffer_bypass(2, bypass); + ok &= usrp()->set_adc_buffer_bypass(3, bypass); + } + return ok; +} + +bool +db_base::set_atr_mask(int v) +{ + // Set Auto T/R mask. + return usrp()->write_atr_mask(d_which, v); +} + +bool +db_base::set_atr_txval(int v) +{ + // Set Auto T/R register value to be used when transmitting. + return usrp()->write_atr_txval(d_which, v); +} + +bool +db_base::set_atr_rxval(int v) +{ + // Set Auto T/R register value to be used when receiving. + return usrp()->write_atr_rxval(d_which, v); +} + +bool +db_base::set_atr_tx_delay(int v) +{ + // Set Auto T/R delay (in clock ticks) from when Tx fifo gets data to + // when T/R switches. + return usrp()->write_atr_tx_delay(v); +} + +bool +db_base::set_atr_rx_delay(int v) +{ + // Set Auto T/R delay (in clock ticks) from when Tx fifo goes empty to + // when T/R switches. + return usrp()->write_atr_rx_delay(v); +} + +bool +db_base::i_and_q_swapped() +{ + // Return True if this is a quadrature device and (for RX) ADC 0 is Q + // or (for TX) DAC 0 is Q + return false; +} + +bool +db_base::spectrum_inverted() +{ + // Return True if the dboard gives an inverted spectrum + + return false; +} + +bool +db_base::set_enable(bool on) +{ + // For tx daughterboards, this controls the transmitter enable. + + return true; // default is nop +} + +bool +db_base::set_auto_tr(bool on) +{ + // Enable automatic Transmit/Receive switching (ATR). + // + // Should be overridden in subclasses that care. This will typically + // set the atr_mask, txval and rxval. + + return true; +} + +bool +db_base::set_lo_offset(double offset) +{ + // Set how much LO is offset from requested frequency + + d_lo_offset = offset; + return true; +} + +bool +db_base::select_rx_antenna(int which_antenna) +{ + // Specify which antenna port to use for reception. + // Should be overriden by daughterboards that care. + + return which_antenna == 0; +} + +bool +db_base::select_rx_antenna(const std::string &which_antenna) +{ + // Specify which antenna port to use for reception. + // Should be overriden by daughterboards that care. + + return which_antenna == ""; +} + + +// Reference Clock section +// +// Control whether a reference clock is sent to the daughterboards, +// and what frequency +// +// Bit 7 -- 1 turns on refclk, 0 allows IO use +// Bits 6:0 Divider value +// + +double +db_base::_refclk_freq() +{ + return usrp()->fpga_master_clock_freq() / _refclk_divisor(); +} + +void +db_base::_enable_refclk(bool enable) +{ + int CLOCK_OUT = 1; // Clock is on lowest bit + int REFCLK_ENABLE = 0x80; + int REFCLK_DIVISOR_MASK = 0x7f; + + if(enable) { + usrp()->_write_oe(d_which, CLOCK_OUT, CLOCK_OUT); // output enable + usrp()->write_refclk(d_which, (_refclk_divisor() & REFCLK_DIVISOR_MASK) | REFCLK_ENABLE); + } + else { + usrp()->write_refclk(d_which, 0); + } +} + +int +db_base::_refclk_divisor() +{ + // Return value to stick in REFCLK_DIVISOR register + throw std::runtime_error("_reflck_divisor() called from base class\n");; +} + + +std::ostream &operator<<(std::ostream &os, db_base &x) +{ + os << x.side_and_name(); + return os; +} diff --git a/usrp/host/lib/legacy/db_base.h b/usrp/host/lib/legacy/db_base.h new file mode 100644 index 000000000..ff0a412e7 --- /dev/null +++ b/usrp/host/lib/legacy/db_base.h @@ -0,0 +1,114 @@ +/* -*- 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 asversion 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_DB_BASE_H +#define INCLUDED_DB_BASE_H + +#include <string> +#include <boost/shared_ptr.hpp> +#include <boost/weak_ptr.hpp> +#include <iosfwd> + +class db_base; +typedef boost::shared_ptr<db_base> db_base_sptr; + +class usrp_basic; +typedef boost::shared_ptr<usrp_basic> usrp_basic_sptr; + +struct freq_result_t +{ + bool ok; + double baseband_freq; +}; + +/******************************************************************************/ + +class db_base +{ + protected: + bool d_is_shutdown; + usrp_basic *d_raw_usrp; + int d_which; + double d_lo_offset; + + void _enable_refclk(bool enable); + virtual double _refclk_freq(); + virtual int _refclk_divisor(); + + usrp_basic *usrp(){ + return d_raw_usrp; + } + + public: + db_base(boost::shared_ptr<usrp_basic> usrp, int which); + virtual ~db_base(); + + int dbid(); + std::string name(); + std::string side_and_name(); + int which() { return d_which; } + + bool bypass_adc_buffers(bool bypass); + bool set_atr_mask(int v); + bool set_atr_txval(int v); + bool set_atr_rxval(int v); + bool set_atr_tx_delay(int v); + bool set_atr_rx_delay(int v); + bool set_lo_offset(double offset); + double lo_offset() { return d_lo_offset; } + + + //////////////////////////////////////////////////////// + // derived classes should override the following methods + +protected: + friend class usrp_basic; + + /*! + * Called to shutdown daughterboard. Called from dtor and usrp_basic dtor. + * + * N.B., any class that overrides shutdown MUST call shutdown in its destructor. + */ + virtual void shutdown(); + + +public: + virtual float gain_min() = 0; + virtual float gain_max() = 0; + virtual float gain_db_per_step() = 0; + virtual double freq_min() = 0; + virtual double freq_max() = 0; + virtual struct freq_result_t set_freq(double target_freq) = 0; + virtual bool set_gain(float gain) = 0; + virtual bool is_quadrature() = 0; + virtual bool i_and_q_swapped(); + virtual bool spectrum_inverted(); + virtual bool set_enable(bool on); + virtual bool set_auto_tr(bool on); + virtual bool select_rx_antenna(int which_antenna); + virtual bool select_rx_antenna(const std::string &which_antenna); +}; + + +std::ostream & operator<<(std::ostream &os, db_base &x); + +#endif /* INCLUDED_DB_BASE_H */ diff --git a/usrp/host/lib/legacy/db_base.i b/usrp/host/lib/legacy/db_base.i new file mode 100644 index 000000000..36bb59a7e --- /dev/null +++ b/usrp/host/lib/legacy/db_base.i @@ -0,0 +1,101 @@ +/* -*- 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 asversion 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 "db_base.h" +%} + +%include <shared_ptr.i> + +class usrp_tune_result +{ +public: + usrp_tune_result(double baseband=0, double dxc=0, + double residual=0, bool inv=0); + ~usrp_tune_result(); + + double baseband_freq; + double dxc_freq; + double residual_freq; + bool inverted; +}; + +struct freq_result_t +{ + bool ok; + double baseband_freq; +}; + +class db_base +{ + private: + db_base(boost::shared_ptr<usrp_basic> usrp, int which); + + public: + virtual ~db_base(); + + int dbid(); + std::string name(); + std::string side_and_name(); + int which() { return d_which; } + + bool bypass_adc_buffers(bool bypass); + bool set_atr_mask(int v); + bool set_atr_txval(int v); + bool set_atr_rxval(int v); + bool set_atr_tx_delay(int v); + bool set_atr_rx_delay(int v); + bool set_lo_offset(double offset); + double lo_offset() { return d_lo_offset; } + + virtual float gain_min() = 0; + virtual float gain_max() = 0; + virtual float gain_db_per_step() = 0; + virtual double freq_min() = 0; + virtual double freq_max() = 0; + virtual struct freq_result_t set_freq(double target_freq) = 0; + virtual bool set_gain(float gain) = 0; + virtual bool is_quadrature() = 0; + virtual bool i_and_q_swapped(); + virtual bool spectrum_inverted(); + virtual bool set_enable(bool on); + virtual bool set_auto_tr(bool on); + virtual bool select_rx_antenna(int which_antenna); + virtual bool select_rx_antenna(const std::string &antenna); +}; + +// Create templates for db's, vectors of db's, and vector of vectors of db's +typedef boost::shared_ptr<db_base> db_base_sptr; +%template(db_base_sptr) boost::shared_ptr<db_base>; +%template(db_base_sptr_vector) std::vector<db_base_sptr>; +%template(db_base_sptr_vector_vector) std::vector<std::vector<db_base_sptr> >; + +// Set better class name in Python +// Enable freq_range and gain_range from public methods of class not implemented in C++ +// And create a dummy wrapper for backwards compatability with some of the example code +%pythoncode %{ + db_base_sptr.__repr__ = lambda self: "<db_base::%s>" % (self.name(),) + db_base_sptr.freq_range = lambda self: (self.freq_min(), self.freq_max(), 1) + db_base_sptr.gain_range = lambda self: (self.gain_min(), self.gain_max(), self.gain_db_per_step()) + +%} diff --git a/usrp/host/lib/legacy/db_base_impl.h b/usrp/host/lib/legacy/db_base_impl.h new file mode 100644 index 000000000..bb4d95d73 --- /dev/null +++ b/usrp/host/lib/legacy/db_base_impl.h @@ -0,0 +1,33 @@ +/* -*- 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 this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef INCLUDED_DB_BASE_IMPL_H +#define INCLUDED_DB_BASE_IMPL_H + +#include <db_base.h> +#include <db_util.h> +#include <usrp_basic.h> +#include <fpga_regs_standard.h> +#include <fpga_regs_common.h> +#include <usrp_prims.h> +#include <usrp_spi_defs.h> +#include <stdexcept> + +#endif /* INCLUDED_DB_BASE_IMPL_H */ diff --git a/usrp/host/lib/legacy/db_basic.cc b/usrp/host/lib/legacy/db_basic.cc new file mode 100644 index 000000000..9c37c438e --- /dev/null +++ b/usrp/host/lib/legacy/db_basic.cc @@ -0,0 +1,263 @@ +// +// 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 asversion 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 <db_basic.h> +#include <db_base_impl.h> + + +db_basic_tx::db_basic_tx(boost::shared_ptr<usrp_basic> usrp, int which) + : db_base(usrp, which) +{ + // Handler for Basic Tx daughterboards. + // + // @param usrp: instance of usrp.source_c + // @param which: which side: 0 or 1 corresponding to TX_A or TX_B respectively + + set_gain((gain_min() + gain_max()) / 2); // initialize gain +} + +db_basic_tx::~db_basic_tx() +{ +} + +double +db_basic_tx::freq_min() +{ + return -90e9; +} + +double +db_basic_tx::freq_max() +{ + return 90e9; +} + +struct freq_result_t +db_basic_tx::set_freq(double target_freq) +{ + // Set the frequency. + // + // @param freq: target RF frequency in Hz + // @type freq: double + // + // @returns (ok, actual_baseband_freq) where: + // ok is True or False and indicates success or failure, + // actual_baseband_freq is the RF frequency that corresponds to DC in the IF. + + struct freq_result_t args = {false, 0}; + args.ok = true; + args.baseband_freq = 0.0; + return args; +} + +float +db_basic_tx::gain_min() +{ + return usrp()->pga_min(); +} + +float +db_basic_tx::gain_max() +{ + return usrp()->pga_max(); +} + +float +db_basic_tx::gain_db_per_step() +{ + return usrp()->pga_db_per_step(); +} + +bool +db_basic_tx::set_gain(float gain) +{ + // Set the gain. + // + // @param gain: gain in decibels + // @returns True/False + + bool ok = usrp()->set_pga(d_which * 2 + 0, gain); + ok = ok && usrp()->set_pga(d_which * 2 + 1, gain); + return ok; +} + +bool +db_basic_tx::is_quadrature() +{ + // Return True if this board requires both I & Q analog channels. + + return true; +} + + +/******************************************************************************/ + + +db_basic_rx::db_basic_rx(usrp_basic_sptr usrp, int which, int subdev) + : db_base(usrp, which) +{ + // Handler for Basic Rx daughterboards. + // + // @param usrp: instance of usrp.source_c + // @param which: which side: 0 or 1 corresponding to TX_A or TX_B respectively + // @param subdev: which analog i/o channel: 0 or 1 + // @type subdev: int + + d_subdev = subdev; + + bypass_adc_buffers(true); + + if(0) { // Doing this would give us a different default than the historical values... + set_gain(float(gain_min() + gain_max()) / 2.0); // initialize gain + } +} + +db_basic_rx::~db_basic_rx() +{ +} + +double +db_basic_rx::freq_min() +{ + return -90e9; +} + +double +db_basic_rx::freq_max() +{ + return 90e9; +} + +struct freq_result_t +db_basic_rx::set_freq(double target_freq) +{ + // Set the frequency. + // + // @param freq: target RF frequency in Hz + // @type freq: double + // + // @returns (ok, actual_baseband_freq) where: + // ok is True or False and indicates success or failure, + // actual_baseband_freq is the RF frequency that corresponds to DC in the IF. + + struct freq_result_t args = {true, 0.0}; + return args; +} + +float +db_basic_rx::gain_min() +{ + return usrp()->pga_min(); +} + +float +db_basic_rx::gain_max() +{ + return usrp()->pga_max(); +} + +float +db_basic_rx::gain_db_per_step() +{ + return usrp()->pga_db_per_step(); +} + +bool +db_basic_rx::set_gain(float gain) +{ + // Set the gain. + // + // @param gain: gain in decibels + // @returns True/False + + return usrp()->set_pga(d_which * 2 + d_subdev, gain); +} + +bool +db_basic_rx::is_quadrature() +{ + // Return True if this board requires both I & Q analog channels. + + // This bit of info is useful when setting up the USRP Rx mux register. + + return false; +} + + + +/******************************************************************************/ + + +db_lf_tx::db_lf_tx(usrp_basic_sptr usrp, int which) + : db_basic_tx(usrp, which) +{ + // Handler for Low Freq Tx daughterboards. + // + // @param usrp: instance of usrp.source_c + // @param which: which side: 0 or 1 corresponding to RX_A or RX_B respectively +} + +db_lf_tx::~db_lf_tx() +{ +} + +double +db_lf_tx::freq_min() +{ + return -32e6; +} + +double +db_lf_tx::freq_max() +{ + return 32e6; +} + +/******************************************************************************/ + + +db_lf_rx::db_lf_rx(usrp_basic_sptr usrp, int which, int subdev) + : db_basic_rx(usrp, which, subdev) +{ + // Handler for Low Freq Rx daughterboards. + // + // @param usrp: instance of usrp.source_c + // @param which: which side: 0 or 1 corresponding to RX_A or RX_B respectively + // @param subdev: which analog i/o channel: 0 or 1 + // @type subdev: int +} + +db_lf_rx::~db_lf_rx() +{ +} + +double +db_lf_rx::freq_min() +{ + return 0.0; +} + +double +db_lf_rx::freq_max() +{ + return 32e6; +} + + diff --git a/usrp/host/lib/legacy/db_basic.h b/usrp/host/lib/legacy/db_basic.h new file mode 100644 index 000000000..4dd92b997 --- /dev/null +++ b/usrp/host/lib/legacy/db_basic.h @@ -0,0 +1,99 @@ +/* -*- 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 asversion 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 DB_BASIC_H +#define DB_BASIC_H + +#include <db_base.h> + + +/******************************************************************************/ + + +class db_basic_tx : public db_base +{ +public: + db_basic_tx(usrp_basic_sptr usrp, int which); + ~db_basic_tx(); + + float gain_min(); + float gain_max(); + float gain_db_per_step(); + double freq_min(); + double freq_max(); + struct freq_result_t set_freq(double target_freq); + bool set_gain(float gain); + bool is_quadrature(); +}; + + +/******************************************************************************/ + + +class db_basic_rx : public db_base +{ + public: + db_basic_rx(usrp_basic_sptr usrp, int which, int subdev); + ~db_basic_rx(); + + float gain_min(); + float gain_max(); + float gain_db_per_step(); + double freq_min(); + double freq_max(); + struct freq_result_t set_freq(double target_freq); + bool set_gain(float gain); + bool is_quadrature(); + +private: + int d_subdev; +}; + + +/******************************************************************************/ + + +class db_lf_rx : public db_basic_rx +{ + public: + db_lf_rx(usrp_basic_sptr usrp, int which, int subdev); + ~db_lf_rx(); + + double freq_min(); + double freq_max(); +}; + + +/******************************************************************************/ + + +class db_lf_tx : public db_basic_tx +{ + public: + db_lf_tx(usrp_basic_sptr usrp, int which); + ~db_lf_tx(); + + double freq_min(); + double freq_max(); +}; + + +#endif diff --git a/usrp/host/lib/legacy/db_boards.cc b/usrp/host/lib/legacy/db_boards.cc new file mode 100644 index 000000000..1ba8b0e7f --- /dev/null +++ b/usrp/host/lib/legacy/db_boards.cc @@ -0,0 +1,215 @@ +/* -*- 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 asversion 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 <db_boards.h> +#include <usrp_dbid.h> +#include <db_basic.h> +#include <db_tv_rx.h> +#include <db_dbs_rx.h> +#include <db_flexrf.h> +#include <db_flexrf_mimo.h> +#include <db_xcvr2450.h> +#include <db_wbx.h> +#include <db_dtt754.h> +#include <db_dtt768.h> + +std::vector<db_base_sptr> +instantiate_dbs(int dbid, usrp_basic_sptr usrp, int which_side) +{ + std::vector<db_base_sptr> db; + + switch(dbid) { + + case(USRP_DBID_BASIC_TX): + db.push_back(db_base_sptr(new db_basic_tx(usrp, which_side))); + break; + + case(USRP_DBID_BASIC_RX): + db.push_back(db_base_sptr(new db_basic_rx(usrp, which_side, 0))); + db.push_back(db_base_sptr(new db_basic_rx(usrp, which_side, 1))); + break; + + case(USRP_DBID_LF_TX): + db.push_back(db_base_sptr(new db_lf_tx(usrp, which_side))); + break; + + case(USRP_DBID_LF_RX): + db.push_back(db_base_sptr(new db_lf_rx(usrp, which_side, 0))); + db.push_back(db_base_sptr(new db_lf_rx(usrp, which_side, 1))); + break; + + case(USRP_DBID_DBS_RX): + db.push_back(db_base_sptr(new db_dbs_rx(usrp, which_side))); + break; + + case(USRP_DBID_TV_RX): + db.push_back(db_base_sptr(new db_tv_rx(usrp, which_side, 43.75e6, 5.75e6))); + break; + case(USRP_DBID_TV_RX_REV_2): + db.push_back(db_base_sptr(new db_tv_rx(usrp, which_side, 44e6, 20e6))); + break; + case(USRP_DBID_TV_RX_REV_3): + db.push_back(db_base_sptr(new db_tv_rx(usrp, which_side, 44e6, 20e6))); + break; + + case(USRP_DBID_FLEX_2400_TX): + db.push_back(db_base_sptr(new db_flexrf_2400_tx(usrp, which_side))); + break; + case(USRP_DBID_FLEX_2400_RX): + db.push_back(db_base_sptr(new db_flexrf_2400_rx(usrp, which_side))); + break; + case(USRP_DBID_FLEX_1200_TX): + db.push_back(db_base_sptr(new db_flexrf_1200_tx(usrp, which_side))); + break; + case(USRP_DBID_FLEX_1200_RX): + db.push_back(db_base_sptr(new db_flexrf_1200_rx(usrp, which_side))); + break; + case(USRP_DBID_FLEX_1800_TX): + db.push_back(db_base_sptr(new db_flexrf_1800_tx(usrp, which_side))); + break; + case(USRP_DBID_FLEX_1800_RX): + db.push_back(db_base_sptr(new db_flexrf_1800_rx(usrp, which_side))); + break; + case(USRP_DBID_FLEX_900_TX): + db.push_back(db_base_sptr(new db_flexrf_900_tx(usrp, which_side))); + break; + case(USRP_DBID_FLEX_900_RX): + db.push_back(db_base_sptr(new db_flexrf_900_rx(usrp, which_side))); + break; + case(USRP_DBID_FLEX_400_TX): + db.push_back(db_base_sptr(new db_flexrf_400_tx(usrp, which_side))); + break; + case(USRP_DBID_FLEX_400_RX): + db.push_back(db_base_sptr(new db_flexrf_400_rx(usrp, which_side))); + break; + case(USRP_DBID_FLEX_2400_TX_MIMO_A): + db.push_back(db_base_sptr(new db_flexrf_2400_tx_mimo_a(usrp, which_side))); + break; + case(USRP_DBID_FLEX_2400_RX_MIMO_A): + db.push_back(db_base_sptr(new db_flexrf_2400_rx_mimo_a(usrp, which_side))); + break; + case(USRP_DBID_FLEX_1800_TX_MIMO_A): + db.push_back(db_base_sptr(new db_flexrf_1800_tx_mimo_a(usrp, which_side))); + break; + case(USRP_DBID_FLEX_1800_RX_MIMO_A): + db.push_back(db_base_sptr(new db_flexrf_1800_rx_mimo_a(usrp, which_side))); + break; + case(USRP_DBID_FLEX_1200_TX_MIMO_A): + db.push_back(db_base_sptr(new db_flexrf_1200_tx_mimo_a(usrp, which_side))); + break; + case(USRP_DBID_FLEX_1200_RX_MIMO_A): + db.push_back(db_base_sptr(new db_flexrf_1200_rx_mimo_a(usrp, which_side))); + break; + case(USRP_DBID_FLEX_900_TX_MIMO_A): + db.push_back(db_base_sptr(new db_flexrf_900_tx_mimo_a(usrp, which_side))); + break; + case(USRP_DBID_FLEX_900_RX_MIMO_A): + db.push_back(db_base_sptr(new db_flexrf_900_rx_mimo_a(usrp, which_side))); + break; + case(USRP_DBID_FLEX_400_TX_MIMO_A): + db.push_back(db_base_sptr(new db_flexrf_400_tx_mimo_a(usrp, which_side))); + break; + case(USRP_DBID_FLEX_400_RX_MIMO_A): + db.push_back(db_base_sptr(new db_flexrf_400_rx_mimo_a(usrp, which_side))); + break; + case(USRP_DBID_FLEX_2400_TX_MIMO_B): + db.push_back(db_base_sptr(new db_flexrf_2400_tx_mimo_b(usrp, which_side))); + break; + case(USRP_DBID_FLEX_2400_RX_MIMO_B): + db.push_back(db_base_sptr(new db_flexrf_2400_rx_mimo_b(usrp, which_side))); + break; + case(USRP_DBID_FLEX_1800_TX_MIMO_B): + db.push_back(db_base_sptr(new db_flexrf_1800_tx_mimo_b(usrp, which_side))); + break; + case(USRP_DBID_FLEX_1800_RX_MIMO_B): + db.push_back(db_base_sptr(new db_flexrf_1800_rx_mimo_b(usrp, which_side))); + break; + case(USRP_DBID_FLEX_1200_TX_MIMO_B): + db.push_back(db_base_sptr(new db_flexrf_1200_tx_mimo_b(usrp, which_side))); + break; + case(USRP_DBID_FLEX_1200_RX_MIMO_B): + db.push_back(db_base_sptr(new db_flexrf_1200_rx_mimo_b(usrp, which_side))); + break; + case(USRP_DBID_FLEX_900_TX_MIMO_B): + db.push_back(db_base_sptr(new db_flexrf_900_tx_mimo_b(usrp, which_side))); + break; + case(USRP_DBID_FLEX_900_RX_MIMO_B): + db.push_back(db_base_sptr(new db_flexrf_900_rx_mimo_b(usrp, which_side))); + break; + case(USRP_DBID_FLEX_400_TX_MIMO_B): + db.push_back(db_base_sptr(new db_flexrf_400_tx_mimo_b(usrp, which_side))); + break; + case(USRP_DBID_FLEX_400_RX_MIMO_B): + db.push_back(db_base_sptr(new db_flexrf_400_rx_mimo_b(usrp, which_side))); + break; + + case(USRP_DBID_XCVR2450_TX): + db.push_back(db_base_sptr(new db_xcvr2450_tx(usrp, which_side))); + break; + case(USRP_DBID_XCVR2450_RX): + db.push_back(db_base_sptr(new db_xcvr2450_rx(usrp, which_side))); + break; + +#if 0 // FIXME wbx doesn't compile + case(USRP_DBID_WBX_LO_TX): + db.push_back(db_base_sptr(new db_wbx_lo_tx(usrp, which_side))); + break; + case(USRP_DBID_WBX_LO_RX): + db.push_back(db_base_sptr(new db_wbx_lo_rx(usrp, which_side))); + break; +#endif + + case(USRP_DBID_DTT754): + db.push_back(db_base_sptr(new db_dtt754(usrp, which_side))); + break; + case(USRP_DBID_DTT768): + db.push_back(db_base_sptr(new db_dtt768(usrp, which_side))); + break; + + case(-1): + if (boost::dynamic_pointer_cast<usrp_basic_tx>(usrp)){ + db.push_back(db_base_sptr(new db_basic_tx(usrp, which_side))); + } + else { + db.push_back(db_base_sptr(new db_basic_rx(usrp, which_side, 0))); + db.push_back(db_base_sptr(new db_basic_rx(usrp, which_side, 1))); + } + break; + + case(-2): + default: + if (boost::dynamic_pointer_cast<usrp_basic_tx>(usrp)){ + fprintf(stderr, "\n\aWarning: Treating daughterboard with invalid EEPROM contents as if it were a \"Basic Tx.\"\n"); + fprintf(stderr, "Warning: This is almost certainly wrong... Use appropriate burn-*-eeprom utility.\n\n"); + db.push_back(db_base_sptr(new db_basic_tx(usrp, which_side))); + } + else { + fprintf(stderr, "\n\aWarning: Treating daughterboard with invalid EEPROM contents as if it were a \"Basic Rx.\"\n"); + fprintf(stderr, "Warning: This is almost certainly wrong... Use appropriate burn-*-eeprom utility.\n\n"); + db.push_back(db_base_sptr(new db_basic_rx(usrp, which_side, 0))); + db.push_back(db_base_sptr(new db_basic_rx(usrp, which_side, 1))); + } + break; + } + + return db; +} diff --git a/usrp/host/lib/legacy/db_boards.h b/usrp/host/lib/legacy/db_boards.h new file mode 100644 index 000000000..037c46037 --- /dev/null +++ b/usrp/host/lib/legacy/db_boards.h @@ -0,0 +1,33 @@ +/* -*- 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 asversion 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 DB_BOARDS_H +#define DB_BOARDS_H + +#include <db_base.h> +#include <usrp_basic.h> + +std::vector<db_base_sptr> instantiate_dbs(int dbid, usrp_basic_sptr usrp, int which_side); + +#endif + + diff --git a/usrp/host/lib/legacy/db_dbs_rx.cc b/usrp/host/lib/legacy/db_dbs_rx.cc new file mode 100644 index 000000000..5f3b32f92 --- /dev/null +++ b/usrp/host/lib/legacy/db_dbs_rx.cc @@ -0,0 +1,491 @@ +// +// 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 asversion 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 <db_dbs_rx.h> +#include <db_base_impl.h> +#include <cmath> + + +/*****************************************************************************/ + + +db_dbs_rx::db_dbs_rx(usrp_basic_sptr _usrp, int which) + : db_base(_usrp, which) +{ + // Control DBS receiver based USRP daughterboard. + // + // @param usrp: instance of usrp.source_c + // @param which: which side: 0, 1 corresponding to RX_A or RX_B respectively + + usrp()->_write_oe(d_which, 0x0001, 0x0001); + if(which == 0) { + d_i2c_addr = 0x67; + } + else { + d_i2c_addr = 0x65; + } + + d_n = 950; + d_div2 = 0; + d_osc = 5; + d_cp = 3; + d_r = 4; + d_r_int = 1; + d_fdac = 127; + d_m = 2; + d_dl = 0; + d_ade = 0; + d_adl = 0; + d_gc1 = 0; + d_gc2 = 31; + d_diag = 0; + + _enable_refclk(true); + + set_gain((gain_min() + gain_max()) / 2.0); // initialize gain + + bypass_adc_buffers(true); +} + +db_dbs_rx::~db_dbs_rx() +{ + shutdown(); +} + +void +db_dbs_rx::shutdown() +{ + if (!d_is_shutdown){ + d_is_shutdown = true; + // do whatever there is to do to shutdown orderly + _enable_refclk(false); + } +} + +void +db_dbs_rx::_write_reg (int regno, int v) +{ + //regno is in [0,5], v is value to write to register""" + assert (0 <= regno && regno <= 5); + std::vector<int> args(2); + args[0] = regno; + args[1] = v; + usrp()->write_i2c (d_i2c_addr, int_seq_to_str (args)); +} + +void +db_dbs_rx::_write_regs (int starting_regno, const std::vector<int> &vals) +{ + // starting_regno is in [0,5], + // vals is a seq of integers to write to consecutive registers""" + + //FIXME + std::vector<int> args; + args.push_back(starting_regno); + args.insert(args.end(), vals.begin(), vals.end()); + usrp()->write_i2c (d_i2c_addr, int_seq_to_str (args)); +} + +std::vector<int> +db_dbs_rx::_read_status () +{ + //If successful, return list of two ints: [status_info, filter_DAC]""" + std::string s = usrp()->read_i2c (d_i2c_addr, 2); + if(s.size() != 2) { + std::vector<int> ret(0); + return ret; + } + return str_to_int_seq (s); +} + +void +db_dbs_rx::_send_reg(int regno) +{ + assert(0 <= regno && regno <= 5); + if(regno == 0) + _write_reg(0,(d_div2<<7) + (d_n>>8)); + if(regno == 1) + _write_reg(1,d_n & 255); + if(regno == 2) + _write_reg(2,d_osc + (d_cp<<3) + (d_r_int<<5)); + if(regno == 3) + _write_reg(3,d_fdac); + if(regno == 4) + _write_reg(4,d_m + (d_dl<<5) + (d_ade<<6) + (d_adl<<7)); + if(regno == 5) + _write_reg(5,d_gc2 + (d_diag<<5)); +} + +// BW setting +void +db_dbs_rx::_set_m(int m) +{ + assert(m>0 && m<32); + d_m = m; + _send_reg(4); +} + +void +db_dbs_rx::_set_fdac(int fdac) +{ + assert(fdac>=0 && fdac<128); + d_fdac = fdac; + _send_reg(3); +} + +struct bw_t +db_dbs_rx::set_bw (float bw) +{ + assert(bw>=1e6 && bw<=33e6); + + struct bw_t ret = {0, 0, 0}; + int m_max, m_min, m_test, fdac_test; + if(bw >= 4e6) + m_max = int(std::min(31, (int)floor(_refclk_freq()/1e6))); + else if(bw >= 2e6) // Outside of Specs! + m_max = int(std::min(31, (int)floor(_refclk_freq()/.5e6))); + else // Way outside of Specs! + m_max = int(std::min(31, (int)floor(_refclk_freq()/.25e6))); + + m_min = int(ceil(_refclk_freq()/2.5e6)); + m_test = m_max; + while(m_test >= m_min) { + fdac_test = static_cast<int>(round(((bw * m_test / _refclk_freq())-4)/.145)); + if(fdac_test > 127) + m_test = m_test - 1; + else + break; + } + + if(m_test>=m_min && fdac_test>=0) { + _set_m(m_test); + _set_fdac(fdac_test); + + ret.m = d_m; + ret.fdac = d_fdac; + ret.div = _refclk_freq()/d_m*(4+0.145*d_fdac); + } + else { + fprintf(stderr, "Failed to set bw\n"); + } + return ret; +} + +// Gain setting +void +db_dbs_rx::_set_dl(int dl) +{ + assert(dl == 0 || dl == 1); + d_dl = dl; + _send_reg(4); +} + +void +db_dbs_rx::_set_gc2(int gc2) +{ + assert(gc2<32 && gc2>=0); + d_gc2 = gc2; + _send_reg(5); +} + +void +db_dbs_rx::_set_gc1(int gc1) +{ + assert(gc1>=0 && gc1<4096); + d_gc1 = gc1; + usrp()->write_aux_dac(d_which, 0, gc1); +} + +void +db_dbs_rx::_set_pga(int pga_gain) +{ + assert(pga_gain>=0 && pga_gain<=20); + if(d_which == 0) { + usrp()->set_pga (0, pga_gain); + usrp()->set_pga (1, pga_gain); + } + else { + usrp()->set_pga (2, pga_gain); + usrp()->set_pga (3, pga_gain); + } +} + +float +db_dbs_rx::gain_min() +{ + return 0; +} + +float +db_dbs_rx::gain_max() +{ + return 104; +} + +float +db_dbs_rx::gain_db_per_step() +{ + return 1; +} + +bool +db_dbs_rx::set_gain(float gain) +{ + // Set the gain. + // + // @param gain: gain in decibels + // @returns True/False + + if(!(gain>=0 && gain<105)) { + throw std::runtime_error("gain out of range\n"); + } + + int gc1=0, gc2=0, dl=0, pga=0; + + if(gain < 56) { + gc1 = int((-gain*1.85/56.0 + 2.6)*4096.0/3.3); + gain = 0; + } + else { + gc1 = 0; + gain -= 56; + } + + if(gain < 24) { + gc2 = static_cast<int>(round(31.0 * (1-gain/24.0))); + gain = 0; + } + else { + gc2 = 0; + gain -=24; + } + + if(gain >= 4.58) { + dl = 1; + gain -= 4.58; + } + + pga = gain; + _set_gc1(gc1); + _set_gc2(gc2); + _set_dl(dl); + _set_pga(pga); + + return true; +} + +// Frequency setting +void +db_dbs_rx::_set_osc(int osc) +{ + assert(osc>=0 && osc<8); + d_osc = osc; + _send_reg(2); +} + +void +db_dbs_rx::_set_cp(int cp) +{ + assert(cp>=0 && cp<4); + d_cp = cp; + _send_reg(2); +} + +void +db_dbs_rx::_set_n(int n) +{ + assert(n>256 && n<32768); + d_n = n; + _send_reg(0); + _send_reg(1); +} + +void +db_dbs_rx::_set_div2(int div2) +{ + assert(div2 == 0 || div2 == 1); + d_div2 = div2; + _send_reg(0); +} + +void +db_dbs_rx::_set_r(int r) +{ + assert(r>=0 && r<128); + d_r = r; + d_r_int = static_cast<int>(round(log10(r)/log10(2)) - 1); + _send_reg(2); +} + +// FIXME How do we handle ADE and ADL properly? +void +db_dbs_rx::_set_ade(int ade) +{ + assert(ade == 0 || ade == 1); + d_ade = ade; + _send_reg(4); +} + +double +db_dbs_rx::freq_min() +{ + return 500e6; +} + +double +db_dbs_rx::freq_max() +{ + return 2.6e9; +} + +struct freq_result_t +db_dbs_rx::set_freq(double freq) +{ + // Set the frequency. + // + // @param freq: target RF frequency in Hz + // @type freq: double + // + // @returns (ok, actual_baseband_freq) where: + // ok is True or False and indicates success or failure, + // actual_baseband_freq is RF frequency that corresponds to DC in the IF. + + freq_result_t args = {false, 0}; + + if(!(freq>=freq_min() && freq<=freq_max())) { + return args; + } + + double vcofreq; + if(freq<1150e6) { + _set_div2(0); + vcofreq = 4 * freq; + } + else { + _set_div2(1); + vcofreq = 2 * freq; + } + + _set_ade(1); + int rmin = std::max(2, (int)(_refclk_freq()/2e6)); + int rmax = std::min(128, (int)(_refclk_freq()/500e3)); + int r = 2; + int n = 0; + int best_r = 2; + int best_n = 0; + int best_delta = 10e6; + int delta; + + while(r <= rmax) { + n = static_cast<int>(round(freq/(_refclk_freq()/r))); + if(r<rmin || n<256) { + r = r * 2; + continue; + } + delta = (int)fabs(n*_refclk_freq()/r - freq); + if(delta < 75e3) { + best_r = r; + best_n = n; + break; + } + if(delta < best_delta*0.9) { + best_r = r; + best_n = n; + best_delta = delta; + } + r = r * 2; + } + _set_r(best_r); + + _set_n(static_cast<int>(round(best_n))); + + int vco; + if(vcofreq < 2433e6) + vco = 0; + else if(vcofreq < 2711e6) + vco=1; + else if(vcofreq < 3025e6) + vco=2; + else if(vcofreq < 3341e6) + vco=3; + else if(vcofreq < 3727e6) + vco=4; + else if(vcofreq < 4143e6) + vco=5; + else if(vcofreq < 4493e6) + vco=6; + else + vco=7; + + _set_osc(vco); + + // Set CP current + int adc_val = 0; + std::vector<int> bytes(2); + while(adc_val == 0 || adc_val == 7) { + bytes = _read_status(); + adc_val = bytes[0] >> 2; + if(adc_val == 0) { + if(vco <= 0) { + return args; + } + else { + vco = vco - 1; + } + } + else if(adc_val == 7) { + if(vco >= 7) { + return args; + } + else { + vco = vco + 1; + } + } + _set_osc(vco); + } + + if(adc_val == 1 || adc_val == 2) { + _set_cp(1); + } + else if(adc_val == 3 || adc_val == 4) { + _set_cp(2); + } + else { + _set_cp(3); + } + + args.ok = true; + args.baseband_freq = d_n * _refclk_freq() / d_r; + return args; +} + +int +db_dbs_rx::_refclk_divisor() +{ + //Return value to stick in REFCLK_DIVISOR register + return 16; +} + +bool +db_dbs_rx::is_quadrature() +{ + // Return True if this board requires both I & Q analog channels. + return true; +} diff --git a/usrp/host/lib/legacy/db_dbs_rx.h b/usrp/host/lib/legacy/db_dbs_rx.h new file mode 100644 index 000000000..f3348af8c --- /dev/null +++ b/usrp/host/lib/legacy/db_dbs_rx.h @@ -0,0 +1,81 @@ +/* -*- 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 asversion 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 DB_DBS_RX_H +#define DB_DBS_RX_H + +#include <db_base.h> +#include <vector> + +struct bw_t { + int m; + int fdac; + float div; +}; + +class db_dbs_rx : public db_base +{ +private: + int d_osc, d_cp, d_n, d_div2, d_r, d_r_int; + int d_fdac, d_m, d_dl, d_ade, d_adl, d_gc1, d_gc2, d_diag; + int d_i2c_addr; + + // Internal gain functions + void _write_reg(int regno, int v); + void _write_regs(int starting_regno, const std::vector<int> &vals); + std::vector<int> _read_status(); + void _send_reg(int regno); + void _set_m(int m); + void _set_fdac(int fdac); + bw_t set_bw(float bw); + void _set_dl(int dl); + void _set_gc2(int gc2); + void _set_gc1(int gc1); + void _set_pga(int pga_gain); + + // Internal frequency function + void _set_osc(int osc); + void _set_cp(int cp); + void _set_n(int n); + void _set_div2(int div2); + void _set_r(int r); + void _set_ade(int ade); + + int _refclk_divisor(); + +protected: + void shutdown(); + +public: + db_dbs_rx(usrp_basic_sptr usrp, int which); + ~db_dbs_rx(); + + float gain_min(); + float gain_max(); + float gain_db_per_step(); + double freq_min(); + double freq_max(); + struct freq_result_t set_freq(double freq); + bool set_gain(float gain); + bool is_quadrature(); +}; + +#endif diff --git a/usrp/host/lib/legacy/db_dtt754.cc b/usrp/host/lib/legacy/db_dtt754.cc new file mode 100644 index 000000000..39f8c3f9e --- /dev/null +++ b/usrp/host/lib/legacy/db_dtt754.cc @@ -0,0 +1,321 @@ +/* -*- 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 asversion 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 <db_dtt754.h> +#include <db_base_impl.h> + +int +control_byte_1() +{ + int RS = 0; // 0 = 166.66kHz reference + int ATP = 7; // Disable internal AGC + return (0x80 | ATP<<3 | RS); +} + +int +control_byte_2() +{ + int STBY = 0; // powered on + int XTO = 1; // turn off xtal out, which we don't have + int ATC = 0; // not clear exactly, possibly speeds up or slows down AGC, which we are not using + + int c = 0xc2 | ATC<<5 | STBY<<4 | XTO; + return c; +} + +int +bandswitch_byte(float freq, float bw) +{ + int P5, CP, BS; + + if(bw>7.5e6) { + P5 = 1; + } + else { + P5 = 0; + } + + if(freq < 121e6) { + CP = 0; + BS = 1; + } + else if(freq < 141e6) { + CP = 1; + BS = 1; + } + else if(freq < 166e6) { + CP = 2; + BS = 1; + } + else if(freq < 182e6) { + CP = 3; + BS = 1; + } + else if(freq < 286e6) { + CP = 0; + BS = 2; + } + else if(freq < 386e6) { + CP = 1; + BS = 2; + } + else if(freq < 446e6) { + CP = 2; + BS = 2; + } + else if(freq < 466e6) { + CP = 3; + BS = 2; + } + else if(freq < 506e6) { + CP = 0; + BS = 8; + } + else if(freq < 761e6) { + CP = 1; + BS = 8; + } + else if(freq < 846e6) { + CP = 2; + BS = 8; + } + else { // limit is ~905 MHz + CP = 3; + BS = 8; + } + return (CP<<6 | P5 << 4 | BS); +} + +db_dtt754::db_dtt754(usrp_basic_sptr _usrp, int which) + : db_base(_usrp, which) +{ + /* + * Control custom DTT75403-based daughterboard. + * + * @param usrp: instance of usrp.source_c + * @param which: which side: 0 or 1 corresponding to RX_A or RX_B respectively + * @type which: int + */ + + // FIXME: DTT754 and DTT768 can probably inherit from a DTT class + + if(d_which == 0) { + d_i2c_addr = 0x60; + } + else { + d_i2c_addr = 0x62; + } + + d_bw = 7e6; + d_IF = 36e6; + + d_f_ref = 166.6666e3; + d_inverted = false; + + set_gain((gain_min() + gain_max()) / 2.0); + + bypass_adc_buffers(false); +} + +db_dtt754::~db_dtt754() +{ +} + +float +db_dtt754::gain_min() +{ + return 0; +} + +float +db_dtt754::gain_max() +{ + return 115; +} + +float +db_dtt754::gain_db_per_step() +{ + return 1; +} + +bool +db_dtt754::set_gain(float gain) +{ + assert(gain>=0 && gain<=115); + + float rfgain, ifgain, pgagain; + if(gain > 60) { + rfgain = 60; + gain = gain - 60; + } + else { + rfgain = gain; + gain = 0; + } + + if(gain > 35) { + ifgain = 35; + gain = gain - 35; + } + else { + ifgain = gain; + gain = 0; + } + pgagain = gain; + + _set_rfagc(rfgain); + _set_ifagc(ifgain); + _set_pga(pgagain); + + return true; // can't fail with the assert in place +} + +double +db_dtt754::freq_min() +{ + return 44e6; +} + +double +db_dtt754::freq_max() +{ + return 900e6; +} + +struct freq_result_t +db_dtt754::set_freq(double target_freq) +{ + /* + * @returns (ok, actual_baseband_freq) where: + * ok is True or False and indicates success or failure, + * actual_baseband_freq is the RF frequency that corresponds to DC in the IF. + */ + + freq_result_t ret = {false, 0.0}; + + if(target_freq < freq_min() || target_freq > freq_max()) { + return ret; + } + + double target_lo_freq = target_freq + d_IF; // High side mixing + + int divisor = (int)(0.5+(target_lo_freq / d_f_ref)); + double actual_lo_freq = d_f_ref*divisor; + + if((divisor & ~0x7fff) != 0) { // must be 15-bits or less + return ret; + } + + // build i2c command string + std::vector<int> buf(5); + buf[0] = (divisor >> 8) & 0xff; // DB1 + buf[1] = divisor & 0xff; // DB2 + buf[2] = control_byte_1(); + buf[3] = bandswitch_byte(actual_lo_freq, d_bw); + buf[4] = control_byte_2(); + + bool ok = usrp()->write_i2c(d_i2c_addr, int_seq_to_str (buf)); + + d_freq = actual_lo_freq - d_IF; + + ret.ok = ok; + ret.baseband_freq = actual_lo_freq; + + return ret; + +} + +bool +db_dtt754::is_quadrature() +{ + /* + * Return True if this board requires both I & Q analog channels. + * + * This bit of info is useful when setting up the USRP Rx mux register. + */ + + return false; +} + +bool +db_dtt754::spectrum_inverted() +{ + /* + * The 43.75 MHz version is inverted + */ + + return d_inverted; +} + +void +db_dtt754::set_bw(float bw) +{ + /* + * Choose the SAW filter bandwidth, either 7MHz or 8MHz) + */ + + d_bw = bw; + set_freq(d_freq); +} + +void +db_dtt754::_set_rfagc(float gain) +{ + assert(gain <= 60 && gain >= 0); + // FIXME this has a 0.5V step between gain = 60 and gain = 59. + // Why are there two cases instead of a single linear case? + float voltage; + if(gain == 60) { + voltage = 4; + } + else { + voltage = gain/60.0 * 2.25 + 1.25; + } + + int dacword = (int)(4096*voltage/1.22/3.3); // 1.22 = opamp gain + + assert(dacword>=0 && dacword<4096); + usrp()->write_aux_dac(d_which, 1, dacword); +} + +void +db_dtt754::_set_ifagc(float gain) +{ + assert(gain <= 35 && gain >= 0); + float voltage = gain/35.0 * 2.1 + 1.4; + int dacword = (int)(4096*voltage/1.22/3.3); // 1.22 = opamp gain + + assert(dacword>=0 && dacword<4096); + usrp()->write_aux_dac(d_which, 0, dacword); +} + +void +db_dtt754::_set_pga(float pga_gain) +{ + assert(pga_gain >=0 && pga_gain <=20); + if(d_which == 0) { + usrp()->set_pga (0, pga_gain); + } + else { + usrp()->set_pga (2, pga_gain); + } +} diff --git a/usrp/host/lib/legacy/db_dtt754.h b/usrp/host/lib/legacy/db_dtt754.h new file mode 100644 index 000000000..93b9164e9 --- /dev/null +++ b/usrp/host/lib/legacy/db_dtt754.h @@ -0,0 +1,57 @@ +/* -*- 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 asversion 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 DB_DTT754_H +#define DB_DTT754_H + +#include <db_base.h> +#include <boost/shared_ptr.hpp> + +class db_dtt754 : public db_base +{ +public: + db_dtt754(usrp_basic_sptr usrp, int which); + ~db_dtt754(); + + float gain_min(); + float gain_max(); + float gain_db_per_step(); + bool set_gain(float gain); + + double freq_min(); + double freq_max(); + struct freq_result_t set_freq(double target_freq); + + bool is_quadrature(); + bool spectrum_inverted(); + void set_bw(float bw); + +private: + void _set_rfagc(float gain); + void _set_ifagc(float gain); + void _set_pga(float pga_gain); + + int d_i2c_addr; + float d_bw, d_freq, d_IF, d_f_ref; + bool d_inverted; +}; + +#endif diff --git a/usrp/host/lib/legacy/db_dtt768.cc b/usrp/host/lib/legacy/db_dtt768.cc new file mode 100644 index 000000000..c2e9c9821 --- /dev/null +++ b/usrp/host/lib/legacy/db_dtt768.cc @@ -0,0 +1,294 @@ +/* -*- 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 asversion 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 <db_dtt768.h> +#include <db_base_impl.h> + +int +control_byte_4() +{ + int C = 0; // Charge Pump Current, no info on how to choose + int R = 4; // 125 kHz fref + + // int ATP = 7; // Disable internal AGC + return (0x80 | C<<5 | R); +} + +int +control_byte_5(float freq, int agcmode = 1) +{ + if(agcmode) { + if(freq < 150e6) { + return 0x3B; + } + else if(freq < 420e6) { + return 0x7E; + } + else { + return 0xB7; + } + } + else { + if(freq < 150e6) { + return 0x39; + } + else if(freq < 420e6) { + return 0x7C; + } + else { + return 0xB5; + } + } +} + +int +control_byte_6() +{ + int ATC = 0; // AGC time constant = 100ms, 1 = 3S + int IFE = 1; // IF AGC amplifier enable + int AT = 0; // AGC control, ??? + + return (ATC << 5 | IFE << 4 | AT); +} + +int +control_byte_7() +{ + int SAS = 1; // SAW Digital mode + int AGD = 1; // AGC disable + int ADS = 0; // AGC detector into ADC converter + int T = 0; // Test mode, undocumented + return (SAS << 7 | AGD << 5 | ADS << 4 | T); +} + +db_dtt768::db_dtt768(usrp_basic_sptr _usrp, int which) + : db_base(_usrp, which) +{ + /* + * Control custom DTT76803-based daughterboard. + * + * @param usrp: instance of usrp.source_c + * @param which: which side: 0 or 1 corresponding to RX_A or RX_B respectively + * @type which: int + */ + + if(d_which == 0) { + d_i2c_addr = 0x60; + } + else { + d_i2c_addr = 0x62; + } + + d_IF = 44e6; + + d_f_ref = 125e3; + d_inverted = false; + + set_gain((gain_min() + gain_max()) / 2.0); + + bypass_adc_buffers(false); +} + +db_dtt768::~db_dtt768() +{ +} + +float +db_dtt768::gain_min() +{ + return 0; +} + +float +db_dtt768::gain_max() +{ + return 115; +} + +float +db_dtt768::gain_db_per_step() +{ + return 1; +} + +bool +db_dtt768::set_gain(float gain) +{ + assert(gain>=0 && gain<=115); + + float rfgain, ifgain, pgagain; + if(gain > 60) { + rfgain = 60; + gain = gain - 60; + } + else { + rfgain = gain; + gain = 0; + } + + if(gain > 35) { + ifgain = 35; + gain = gain - 35; + } + else { + ifgain = gain; + gain = 0; + } + pgagain = gain; + + _set_rfagc(rfgain); + _set_ifagc(ifgain); + _set_pga(pgagain); + + return true; +} + +double +db_dtt768::freq_min() +{ + return 44e6; +} + +double +db_dtt768::freq_max() +{ + return 900e6; +} + +struct freq_result_t +db_dtt768::set_freq(double target_freq) +{ + /* + * @returns (ok, actual_baseband_freq) where: + * ok is True or False and indicates success or failure, + * actual_baseband_freq is the RF frequency that corresponds to DC in the IF. + */ + + freq_result_t ret = {false, 0.0}; + + if(target_freq < freq_min() || target_freq > freq_max()) { + return ret; + } + + double target_lo_freq = target_freq + d_IF; // High side mixing + + int divisor = (int)(0.5+(target_lo_freq / d_f_ref)); + double actual_lo_freq = d_f_ref*divisor; + + if((divisor & ~0x7fff) != 0) { // must be 15-bits or less + return ret; + } + + // build i2c command string + std::vector<int> buf(6); + buf[0] = (divisor >> 8) & 0xff; // DB1 + buf[1] = divisor & 0xff; // DB2 + buf[2] = control_byte_4(); + buf[3] = control_byte_5(target_freq); + buf[4] = control_byte_6(); + buf[5] = control_byte_7(); + + bool ok = usrp()->write_i2c(d_i2c_addr, int_seq_to_str (buf)); + + d_freq = actual_lo_freq - d_IF; + + ret.ok = ok; + ret.baseband_freq = actual_lo_freq; + + return ret; + +} + +bool +db_dtt768::is_quadrature() +{ + /* + * Return True if this board requires both I & Q analog channels. + * + * This bit of info is useful when setting up the USRP Rx mux register. + */ + + return false; +} + +bool +db_dtt768::spectrum_inverted() +{ + /* + * The 43.75 MHz version is inverted + */ + + return d_inverted; +} + +void +db_dtt768::set_bw(float bw) +{ + /* + * Choose the SAW filter bandwidth, either 7MHz or 8MHz) + */ + + d_bw = bw; + set_freq(d_freq); +} + +void +db_dtt768::_set_rfagc(float gain) +{ + assert(gain <= 60 && gain >= 0); + // FIXME this has a 0.5V step between gain = 60 and gain = 59. + // Why are there two cases instead of a single linear case? + float voltage; + if(gain == 60) { + voltage = 4; + } + else { + voltage = gain/60.0 * 2.25 + 1.25; + } + + int dacword = (int)(4096*voltage/1.22/3.3); // 1.22 = opamp gain + + assert(dacword>=0 && dacword<4096); + usrp()->write_aux_dac(d_which, 1, dacword); +} + +void +db_dtt768::_set_ifagc(float gain) +{ + assert(gain <= 35 && gain >= 0); + float voltage = gain/35.0 * 2.1 + 1.4; + int dacword = (int)(4096*voltage/1.22/3.3); // 1.22 = opamp gain + + assert(dacword>=0 && dacword<4096); + usrp()->write_aux_dac(d_which, 0, dacword); +} + +void +db_dtt768::_set_pga(float pga_gain) +{ + assert(pga_gain >=0 && pga_gain <=20); + if(d_which == 0) { + usrp()->set_pga (0, pga_gain); + } + else { + usrp()->set_pga (2, pga_gain); + } +} diff --git a/usrp/host/lib/legacy/db_dtt768.h b/usrp/host/lib/legacy/db_dtt768.h new file mode 100644 index 000000000..b5560437b --- /dev/null +++ b/usrp/host/lib/legacy/db_dtt768.h @@ -0,0 +1,57 @@ +/* -*- 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 asversion 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 DB_DTT768_H +#define DB_DTT768_H + +#include <db_base.h> +#include <boost/shared_ptr.hpp> + +class db_dtt768 : public db_base +{ +public: + db_dtt768(usrp_basic_sptr usrp, int which); + ~db_dtt768(); + + float gain_min(); + float gain_max(); + float gain_db_per_step(); + bool set_gain(float gain); + + double freq_min(); + double freq_max(); + struct freq_result_t set_freq(double target_freq); + + bool is_quadrature(); + bool spectrum_inverted(); + void set_bw(float bw); + +private: + void _set_rfagc(float gain); + void _set_ifagc(float gain); + void _set_pga(float pga_gain); + + int d_i2c_addr; + float d_bw, d_freq, d_IF, d_f_ref; + bool d_inverted; +}; + +#endif diff --git a/usrp/host/lib/legacy/db_flexrf.cc b/usrp/host/lib/legacy/db_flexrf.cc new file mode 100644 index 000000000..662d9095c --- /dev/null +++ b/usrp/host/lib/legacy/db_flexrf.cc @@ -0,0 +1,1148 @@ +// +// 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 asversion 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 <db_flexrf.h> +#include <db_base_impl.h> + +// d'board i/o pin defs +// Tx and Rx have shared defs, but different i/o regs +#define AUX_RXAGC (1 << 8) +#define POWER_UP (1 << 7) // enables power supply +#define RX_TXN (1 << 6) // Tx only: T/R antenna switch for TX/RX port +#define RX2_RX1N (1 << 6) // Rx only: antenna switch between RX2 and TX/RX port +#define ENABLE (1 << 5) // enables mixer +#define AUX_SEN (1 << 4) +#define AUX_SCLK (1 << 3) +#define PLL_LOCK_DETECT (1 << 2) +#define AUX_SDO (1 << 1) +#define CLOCK_OUT (1 << 0) + +flexrf_base::flexrf_base(usrp_basic_sptr _usrp, int which, int _power_on) + : db_base(_usrp, which), d_power_on(_power_on) +{ + /* + @param usrp: instance of usrp.source_c + @param which: which side: 0 or 1 corresponding to side A or B respectively + @type which: int + */ + + d_first = true; + d_spi_format = SPI_FMT_MSB | SPI_FMT_HDR_0; + + usrp()->_write_oe(d_which, 0, 0xffff); // turn off all outputs + _enable_refclk(false); // disable refclk + + set_auto_tr(false); +} + +flexrf_base::~flexrf_base() +{ + delete d_common; +} + +void +flexrf_base::_write_all(int R, int control, int N) +{ + /* + Write R counter latch, control latch and N counter latch to VCO. + + Adds 10ms delay between writing control and N if this is first call. + This is the required power-up sequence. + + @param R: 24-bit R counter latch + @type R: int + @param control: 24-bit control latch + @type control: int + @param N: 24-bit N counter latch + @type N: int + */ + timespec t; + t.tv_sec = 0; + t.tv_nsec = 10000000; + + _write_R(R); + _write_control(control); + if(d_first) { + //time.sleep(0.010); + nanosleep(&t, NULL); + d_first = false; + } + _write_N(N); +} + +void +flexrf_base::_write_control(int control) +{ + _write_it((control & ~0x3) | 0); +} + +void +flexrf_base::_write_R(int R) +{ + _write_it((R & ~0x3) | 1); +} + +void +flexrf_base::_write_N(int N) +{ + _write_it((N & ~0x3) | 2); +} + +void +flexrf_base::_write_it(int v) +{ + char s[3]; + s[0] = (char)((v >> 16) & 0xff); + s[1] = (char)((v >> 8) & 0xff); + s[2] = (char)(v & 0xff); + std::string str(s, 3); + usrp()->_write_spi(0, d_spi_enable, d_spi_format, str); +} + +bool +flexrf_base::_lock_detect() +{ + /* + @returns: the value of the VCO/PLL lock detect bit. + @rtype: 0 or 1 + */ + if(usrp()->read_io(d_which) & PLL_LOCK_DETECT) { + return true; + } + else { // Give it a second chance + // FIXME: make portable sleep + timespec t; + t.tv_sec = 0; + t.tv_nsec = 100000000; + nanosleep(&t, NULL); + + if(usrp()->read_io(d_which) & PLL_LOCK_DETECT) { + return true; + } + else { + return false; + } + } +} + +bool +flexrf_base::_compute_regs(double freq, int &retR, int &retcontrol, + int &retN, double &retfreq) +{ + /* + Determine values of R, control, and N registers, along with actual freq. + + @param freq: target frequency in Hz + @type freq: float + @returns: (R, control, N, actual_freq) + @rtype: tuple(int, int, int, float) + + Override this in derived classes. + */ + + //raise NotImplementedError; + throw std::runtime_error("_compute_regs called from flexrf_base\n"); +} + +int +flexrf_base::_compute_control_reg() +{ + return d_common->_compute_control_reg(); +} + +int +flexrf_base::_refclk_divisor() +{ + return d_common->_refclk_divisor(); +} + +double +flexrf_base::_refclk_freq() +{ + return 64e6/_refclk_divisor(); +} + +struct freq_result_t +flexrf_base::set_freq(double freq) +{ + /* + @returns (ok, actual_baseband_freq) where: + ok is True or False and indicates success or failure, + actual_baseband_freq is the RF frequency that corresponds to DC in the IF. + */ + + struct freq_result_t args = {false, 0}; + + // Offsetting the LO helps get the Tx carrier leakage out of the way. + // This also ensures that on Rx, we're not getting hosed by the + // FPGA's DC removal loop's time constant. We were seeing a + // problem when running with discontinuous transmission. + // Offsetting the LO made the problem go away. + freq += d_lo_offset; + + int R, control, N; + double actual_freq; + _compute_regs(freq, R, control, N, actual_freq); + + if(R==0) { + return args; + } + + _write_all(R, control, N); + args.ok = _lock_detect(); + args.baseband_freq = actual_freq; + return args; +} + +bool +flexrf_base::_set_pga(float pga_gain) +{ + if(d_which == 0) { + usrp()->set_pga(0, pga_gain); + usrp()->set_pga(1, pga_gain); + } + else { + usrp()->set_pga(2, pga_gain); + usrp()->set_pga(3, pga_gain); + } + return true; +} + +bool +flexrf_base::is_quadrature() +{ + /* + Return True if this board requires both I & Q analog channels. + + This bit of info is useful when setting up the USRP Rx mux register. + */ + return true; +} + +double +flexrf_base::freq_min() +{ + return d_common->freq_min(); +} + +double +flexrf_base::freq_max() +{ + return d_common->freq_max(); +} + +// ---------------------------------------------------------------- + +flexrf_base_tx::flexrf_base_tx(usrp_basic_sptr _usrp, int which, int _power_on) + : flexrf_base(_usrp, which, _power_on) +{ + /* + @param usrp: instance of usrp.sink_c + @param which: 0 or 1 corresponding to side TX_A or TX_B respectively. + */ + + if(which == 0) { + d_spi_enable = SPI_ENABLE_TX_A; + } + else { + d_spi_enable = SPI_ENABLE_TX_B; + } + + // power up the transmit side, but don't enable the mixer + usrp()->_write_oe(d_which,(POWER_UP|RX_TXN|ENABLE), 0xffff); + usrp()->write_io(d_which, (power_on()|RX_TXN), (POWER_UP|RX_TXN|ENABLE)); + set_lo_offset(4e6); + + set_gain((gain_min() + gain_max()) / 2.0); // initialize gain +} + +flexrf_base_tx::~flexrf_base_tx() +{ + shutdown(); +} + + +void +flexrf_base_tx::shutdown() +{ + // fprintf(stderr, "flexrf_base_tx::shutdown d_is_shutdown = %d\n", d_is_shutdown); + + if (!d_is_shutdown){ + d_is_shutdown = true; + // do whatever there is to do to shutdown + + // Power down and leave the T/R switch in the R position + usrp()->write_io(d_which, (power_off()|RX_TXN), (POWER_UP|RX_TXN|ENABLE)); + + // Power down VCO/PLL + d_PD = 3; + + _write_control(_compute_control_reg()); + _enable_refclk(false); // turn off refclk + set_auto_tr(false); + } +} + +bool +flexrf_base_tx::set_auto_tr(bool on) +{ + bool ok = true; + if(on) { + ok &= set_atr_mask (RX_TXN | ENABLE); + ok &= set_atr_txval(0 | ENABLE); + ok &= set_atr_rxval(RX_TXN | 0); + } + else { + ok &= set_atr_mask (0); + ok &= set_atr_txval(0); + ok &= set_atr_rxval(0); + } + return ok; +} + +bool +flexrf_base_tx::set_enable(bool on) +{ + /* + Enable transmitter if on is true + */ + + int v; + int mask = RX_TXN | ENABLE; + if(on) { + v = ENABLE; + } + else { + v = RX_TXN; + } + return usrp()->write_io(d_which, v, mask); +} + +float +flexrf_base_tx::gain_min() +{ + return usrp()->pga_max(); +} + +float +flexrf_base_tx::gain_max() +{ + return usrp()->pga_max(); +} + +float +flexrf_base_tx::gain_db_per_step() +{ + return 1; +} + +bool +flexrf_base_tx::set_gain(float gain) +{ + /* + Set the gain. + + @param gain: gain in decibels + @returns True/False + */ + return _set_pga(usrp()->pga_max()); +} + + +/**************************************************************************/ + + +flexrf_base_rx::flexrf_base_rx(usrp_basic_sptr _usrp, int which, int _power_on) + : flexrf_base(_usrp, which, _power_on) +{ + /* + @param usrp: instance of usrp.source_c + @param which: 0 or 1 corresponding to side RX_A or RX_B respectively. + */ + + if(which == 0) { + d_spi_enable = SPI_ENABLE_RX_A; + } + else { + d_spi_enable = SPI_ENABLE_RX_B; + } + + usrp()->_write_oe(d_which, (POWER_UP|RX2_RX1N|ENABLE), 0xffff); + usrp()->write_io(d_which, (power_on()|RX2_RX1N|ENABLE), + (POWER_UP|RX2_RX1N|ENABLE)); + + // set up for RX on TX/RX port + select_rx_antenna("TX/RX"); + + bypass_adc_buffers(true); + + set_lo_offset(-4e6); +} + +flexrf_base_rx::~flexrf_base_rx() +{ + shutdown(); +} + +void +flexrf_base_rx::shutdown() +{ + // fprintf(stderr, "flexrf_base_rx::shutdown d_is_shutdown = %d\n", d_is_shutdown); + + if (!d_is_shutdown){ + d_is_shutdown = true; + // do whatever there is to do to shutdown + + // Power down + usrp()->common_write_io(C_RX, d_which, power_off(), (POWER_UP|ENABLE)); + + // Power down VCO/PLL + d_PD = 3; + + + // fprintf(stderr, "flexrf_base_rx::shutdown before _write_control\n"); + _write_control(_compute_control_reg()); + + // fprintf(stderr, "flexrf_base_rx::shutdown before _enable_refclk\n"); + _enable_refclk(false); // turn off refclk + + // fprintf(stderr, "flexrf_base_rx::shutdown before set_auto_tr\n"); + set_auto_tr(false); + + // fprintf(stderr, "flexrf_base_rx::shutdown after set_auto_tr\n"); + } +} + +bool +flexrf_base_rx::set_auto_tr(bool on) +{ + bool ok = true; + if(on) { + ok &= set_atr_mask (ENABLE); + ok &= set_atr_txval( 0); + ok &= set_atr_rxval(ENABLE); + } + else { + ok &= set_atr_mask (0); + ok &= set_atr_txval(0); + ok &= set_atr_rxval(0); + } + return true; +} + +bool +flexrf_base_rx::select_rx_antenna(int which_antenna) +{ + /* + Specify which antenna port to use for reception. + @param which_antenna: either 'TX/RX' or 'RX2' + */ + + if(which_antenna == 0) { + usrp()->write_io(d_which, 0,RX2_RX1N); + } + else if(which_antenna == 1) { + usrp()->write_io(d_which, RX2_RX1N, RX2_RX1N); + } + else { + return false; + // throw std::invalid_argument("which_antenna must be either 'TX/RX' or 'RX2'\n"); + } + return true; +} + +bool +flexrf_base_rx::select_rx_antenna(const std::string &which_antenna) +{ + /* + Specify which antenna port to use for reception. + @param which_antenna: either 'TX/RX' or 'RX2' + */ + + if(which_antenna == "TX/RX") { + usrp()->write_io(d_which, 0, RX2_RX1N); + } + else if(which_antenna == "RX2") { + usrp()->write_io(d_which, RX2_RX1N, RX2_RX1N); + } + else { + // throw std::invalid_argument("which_antenna must be either 'TX/RX' or 'RX2'\n"); + return false; + } + return true; +} + +bool +flexrf_base_rx::set_gain(float gain) +{ + /* + Set the gain. + + @param gain: gain in decibels + @returns True/False + */ + + // clamp gain + gain = std::max(gain_min(), std::min(gain, gain_max())); + + float pga_gain, agc_gain; + float V_maxgain, V_mingain, V_fullscale, dac_value; + + float maxgain = gain_max() - usrp()->pga_max(); + float mingain = gain_min(); + if(gain > maxgain) { + pga_gain = gain-maxgain; + assert(pga_gain <= usrp()->pga_max()); + agc_gain = maxgain; + } + else { + pga_gain = 0; + agc_gain = gain; + } + + V_maxgain = .2; + V_mingain = 1.2; + V_fullscale = 3.3; + dac_value = (agc_gain*(V_maxgain-V_mingain)/(maxgain-mingain) + V_mingain)*4096/V_fullscale; + + assert(dac_value>=0 && dac_value<4096); + + return (usrp()->write_aux_dac(d_which, 0, int(dac_value)) + && _set_pga(int(pga_gain))); +} + +// ---------------------------------------------------------------- + + +_AD4360_common::_AD4360_common() +{ + // R-Register Common Values + d_R_RSV = 0; // bits 23,22 + d_BSC = 3; // bits 21,20 Div by 8 to be safe + d_TEST = 0; // bit 19 + d_LDP = 1; // bit 18 + d_ABP = 0; // bit 17,16 3ns + + // N-Register Common Values + d_N_RSV = 0; // bit 7 + + // Control Register Common Values + d_PD = 0; // bits 21,20 Normal operation + d_PL = 0; // bits 13,12 11mA + d_MTLD = 1; // bit 11 enabled + d_CPG = 0; // bit 10 CP setting 1 + d_CP3S = 0; // bit 9 Normal + d_PDP = 1; // bit 8 Positive + d_MUXOUT = 1; // bits 7:5 Digital Lock Detect + d_CR = 0; // bit 4 Normal + d_PC = 1; // bits 3,2 Core power 10mA +} + +_AD4360_common::~_AD4360_common() +{ +} + +bool +_AD4360_common::_compute_regs(double refclk_freq, double freq, int &retR, + int &retcontrol, int &retN, double &retfreq) +{ + /* + Determine values of R, control, and N registers, along with actual freq. + + @param freq: target frequency in Hz + @type freq: float + @returns: (R, control, N, actual_freq) + @rtype: tuple(int, int, int, float) + */ + + // Band-specific N-Register Values + //float phdet_freq = _refclk_freq()/d_R_DIV; + double phdet_freq = refclk_freq/d_R_DIV; + double desired_n = round(freq*d_freq_mult/phdet_freq); + double actual_freq = desired_n * phdet_freq; + int B = floor(desired_n/_prescaler()); + int A = desired_n - _prescaler()*B; + d_B_DIV = int(B); // bits 20:8 + d_A_DIV = int(A); // bit 6:2 + + //assert db_B_DIV >= db_A_DIV + if(d_B_DIV < d_A_DIV) { + retR = 0; + retcontrol = 0; + retN = 0; + retfreq = 0; + return false; + } + + int R = (d_R_RSV<<22) | (d_BSC<<20) | (d_TEST<<19) | + (d_LDP<<18) | (d_ABP<<16) | (d_R_DIV<<2); + + int control = _compute_control_reg(); + + int N = (d_DIVSEL<<23) | (d_DIV2<<22) | (d_CPGAIN<<21) | + (d_B_DIV<<8) | (d_N_RSV<<7) | (d_A_DIV<<2); + + retR = R; + retcontrol = control; + retN = N; + retfreq = actual_freq/d_freq_mult; + return true; +} + +int +_AD4360_common::_compute_control_reg() +{ + int control = (d_P<<22) | (d_PD<<20) | (d_CP2<<17) | (d_CP1<<14) + | (d_PL<<12) | (d_MTLD<<11) | (d_CPG<<10) | (d_CP3S<<9) | (d_PDP<<8) + | (d_MUXOUT<<5) | (d_CR<<4) | (d_PC<<2); + + return control; +} + +int +_AD4360_common::_refclk_divisor() +{ + /* + Return value to stick in REFCLK_DIVISOR register + */ + return 1; +} + +int +_AD4360_common::_prescaler() +{ + if(d_P == 0) { + return 8; + } + else if(d_P == 1) { + return 16; + } + else { + return 32; + } +} + +//---------------------------------------------------------------------- + +_2400_common::_2400_common() + : _AD4360_common() +{ + // Band-specific R-Register Values + d_R_DIV = 16; // bits 15:2 + + // Band-specific C-Register values + d_P = 1; // bits 23,22 Div by 16/17 + d_CP2 = 7; // bits 19:17 + d_CP1 = 7; // bits 16:14 + + // Band specifc N-Register Values + d_DIVSEL = 0; // bit 23 + d_DIV2 = 0; // bit 22 + d_CPGAIN = 0; // bit 21 + d_freq_mult = 1; +} + +double +_2400_common::freq_min() +{ + return 2300e6; +} + +double +_2400_common::freq_max() +{ + return 2700e6; +} + +//---------------------------------------------------------------------- + +_1200_common::_1200_common() + : _AD4360_common() +{ + // Band-specific R-Register Values + d_R_DIV = 16; // bits 15:2 DIV by 16 for a 1 MHz phase detector freq + + // Band-specific C-Register values + d_P = 1; // bits 23,22 Div by 16/17 + d_CP2 = 7; // bits 19:17 1.25 mA + d_CP1 = 7; // bits 16:14 1.25 mA + + // Band specifc N-Register Values + d_DIVSEL = 0; // bit 23 + d_DIV2 = 1; // bit 22 + d_CPGAIN = 0; // bit 21 + d_freq_mult = 2; +} + +double +_1200_common::freq_min() +{ + return 1150e6; +} + +double +_1200_common::freq_max() +{ + return 1350e6; +} + +//------------------------------------------------------------------------- + +_1800_common::_1800_common() + : _AD4360_common() +{ + // Band-specific R-Register Values + d_R_DIV = 16; // bits 15:2 DIV by 16 for a 1 MHz phase detector freq + + // Band-specific C-Register values + d_P = 1; // bits 23,22 Div by 16/17 + d_CP2 = 7; // bits 19:17 1.25 mA + d_CP1 = 7; // bits 16:14 1.25 mA + + // Band specifc N-Register Values + d_DIVSEL = 0; // bit 23 + d_DIV2 = 0; // bit 22 + d_freq_mult = 1; + d_CPGAIN = 0; // bit 21 +} + +double +_1800_common::freq_min() +{ + return 1600e6; +} + +double +_1800_common::freq_max() +{ + return 2000e6; +} + +//------------------------------------------------------------------------- + +_900_common::_900_common() + : _AD4360_common() +{ + // Band-specific R-Register Values + d_R_DIV = 16; // bits 15:2 DIV by 16 for a 1 MHz phase detector freq + + // Band-specific C-Register values + d_P = 1; // bits 23,22 Div by 16/17 + d_CP2 = 7; // bits 19:17 1.25 mA + d_CP1 = 7; // bits 16:14 1.25 mA + + // Band specifc N-Register Values + d_DIVSEL = 0; // bit 23 + d_DIV2 = 1; // bit 22 + d_freq_mult = 2; + d_CPGAIN = 0; // bit 21 +} + +double +_900_common::freq_min() +{ + return 800e6; +} + +double +_900_common::freq_max() +{ + return 1000e6; +} + +//------------------------------------------------------------------------- + +_400_common::_400_common() + : _AD4360_common() +{ + // Band-specific R-Register Values + d_R_DIV = 16; // bits 15:2 + + // Band-specific C-Register values + d_P = 0; // bits 23,22 Div by 8/9 + d_CP2 = 7; // bits 19:17 1.25 mA + d_CP1 = 7; // bits 16:14 1.25 mA + + // Band specifc N-Register Values These are different for TX/RX + d_DIVSEL = 0; // bit 23 + d_freq_mult = 2; + + d_CPGAIN = 0; // bit 21 +} + +double +_400_common::freq_min() +{ + return 400e6; +} + +double +_400_common::freq_max() +{ + return 500e6; +} + +_400_tx::_400_tx() + : _400_common() +{ + d_DIV2 = 1; // bit 22 +} + +_400_rx::_400_rx() + : _400_common() +{ + d_DIV2 = 0; // bit 22 // RX side has built-in DIV2 in AD8348 +} + +//------------------------------------------------------------ + +db_flexrf_2400_tx::db_flexrf_2400_tx(usrp_basic_sptr usrp, int which) + : flexrf_base_tx(usrp, which) +{ + d_common = new _2400_common(); +} + +db_flexrf_2400_tx::~db_flexrf_2400_tx() +{ +} + +bool +db_flexrf_2400_tx::_compute_regs(double freq, int &retR, int &retcontrol, + int &retN, double &retfreq) +{ + return d_common->_compute_regs(_refclk_freq(), freq, retR, + retcontrol, retN, retfreq); +} + + + +db_flexrf_2400_rx::db_flexrf_2400_rx(usrp_basic_sptr usrp, int which) + : flexrf_base_rx(usrp, which) +{ + d_common = new _2400_common(); + set_gain((gain_min() + gain_max()) / 2.0); // initialize gain +} + +db_flexrf_2400_rx::~db_flexrf_2400_rx() +{ +} + +float +db_flexrf_2400_rx::gain_min() +{ + return usrp()->pga_min(); +} + +float +db_flexrf_2400_rx::gain_max() +{ + return usrp()->pga_max()+70; +} + +float +db_flexrf_2400_rx::gain_db_per_step() +{ + return 0.05; +} + + +bool +db_flexrf_2400_rx::i_and_q_swapped() +{ + return true; +} + +bool +db_flexrf_2400_rx::_compute_regs(double freq, int &retR, int &retcontrol, + int &retN, double &retfreq) +{ + return d_common->_compute_regs(_refclk_freq(), freq, retR, + retcontrol, retN, retfreq); +} + +//------------------------------------------------------------ + + +db_flexrf_1200_tx::db_flexrf_1200_tx(usrp_basic_sptr usrp, int which) + : flexrf_base_tx(usrp, which) +{ + d_common = new _1200_common(); +} + +db_flexrf_1200_tx::~db_flexrf_1200_tx() +{ +} + +bool +db_flexrf_1200_tx::_compute_regs(double freq, int &retR, int &retcontrol, + int &retN, double &retfreq) +{ + return d_common->_compute_regs(_refclk_freq(), freq, retR, + retcontrol, retN, retfreq); +} + + + + +db_flexrf_1200_rx::db_flexrf_1200_rx(usrp_basic_sptr usrp, int which) + : flexrf_base_rx(usrp, which) +{ + d_common = new _1200_common(); + set_gain((gain_min() + gain_max()) / 2.0); // initialize gain +} + +db_flexrf_1200_rx::~db_flexrf_1200_rx() +{ +} + +float +db_flexrf_1200_rx::gain_min() +{ + return usrp()->pga_min(); +} + +float +db_flexrf_1200_rx::gain_max() +{ + return usrp()->pga_max()+70; +} + +float +db_flexrf_1200_rx::gain_db_per_step() +{ + return 0.05; +} + +bool +db_flexrf_1200_rx::i_and_q_swapped() +{ + return true; +} + +bool +db_flexrf_1200_rx::_compute_regs(double freq, int &retR, int &retcontrol, + int &retN, double &retfreq) +{ + return d_common->_compute_regs(_refclk_freq(), freq, retR, + retcontrol, retN, retfreq); +} + + +//------------------------------------------------------------ + + +db_flexrf_1800_tx::db_flexrf_1800_tx(usrp_basic_sptr usrp, int which) + : flexrf_base_tx(usrp, which) +{ + d_common = new _1800_common(); +} + +db_flexrf_1800_tx::~db_flexrf_1800_tx() +{ +} + +bool +db_flexrf_1800_tx::_compute_regs(double freq, int &retR, int &retcontrol, + int &retN, double &retfreq) +{ + return d_common->_compute_regs(_refclk_freq(), freq, retR, + retcontrol, retN, retfreq); +} + + + +db_flexrf_1800_rx::db_flexrf_1800_rx(usrp_basic_sptr usrp, int which) + : flexrf_base_rx(usrp, which) +{ + d_common = new _1800_common(); + set_gain((gain_min() + gain_max()) / 2.0); // initialize gain +} + +db_flexrf_1800_rx::~db_flexrf_1800_rx() +{ +} + + +float +db_flexrf_1800_rx::gain_min() +{ + return usrp()->pga_min(); +} + +float +db_flexrf_1800_rx::gain_max() +{ + return usrp()->pga_max()+70; +} + +float +db_flexrf_1800_rx::gain_db_per_step() +{ + return 0.05; +} + +bool +db_flexrf_1800_rx::i_and_q_swapped() +{ + return true; +} + +bool +db_flexrf_1800_rx::_compute_regs(double freq, int &retR, int &retcontrol, + int &retN, double &retfreq) +{ + return d_common->_compute_regs(_refclk_freq(), freq, retR, + retcontrol, retN, retfreq); +} + + +//------------------------------------------------------------ + + +db_flexrf_900_tx::db_flexrf_900_tx(usrp_basic_sptr usrp, int which) + : flexrf_base_tx(usrp, which) +{ + d_common = new _900_common(); +} + +db_flexrf_900_tx::~db_flexrf_900_tx() +{ +} + +bool +db_flexrf_900_tx::_compute_regs(double freq, int &retR, int &retcontrol, + int &retN, double &retfreq) +{ + return d_common->_compute_regs(_refclk_freq(), freq, retR, + retcontrol, retN, retfreq); +} + + +db_flexrf_900_rx::db_flexrf_900_rx(usrp_basic_sptr usrp, int which) + : flexrf_base_rx(usrp, which) +{ + d_common = new _900_common(); + set_gain((gain_min() + gain_max()) / 2.0); // initialize gain +} + +db_flexrf_900_rx::~db_flexrf_900_rx() +{ +} + +float +db_flexrf_900_rx::gain_min() +{ + return usrp()->pga_min(); +} + +float +db_flexrf_900_rx::gain_max() +{ + return usrp()->pga_max()+70; +} + +float +db_flexrf_900_rx::gain_db_per_step() +{ + return 0.05; +} + +bool +db_flexrf_900_rx::i_and_q_swapped() +{ + return true; +} + +bool +db_flexrf_900_rx::_compute_regs(double freq, int &retR, int &retcontrol, + int &retN, double &retfreq) +{ + return d_common->_compute_regs(_refclk_freq(), freq, retR, + retcontrol, retN, retfreq); +} + +//------------------------------------------------------------ + + +db_flexrf_400_tx::db_flexrf_400_tx(usrp_basic_sptr usrp, int which) + : flexrf_base_tx(usrp, which, POWER_UP) +{ + d_common = new _400_tx(); +} + +db_flexrf_400_tx::~db_flexrf_400_tx() +{ +} + +bool +db_flexrf_400_tx::_compute_regs(double freq, int &retR, int &retcontrol, + int &retN, double &retfreq) +{ + return d_common->_compute_regs(_refclk_freq(), freq, retR, + retcontrol, retN, retfreq); +} + + + +db_flexrf_400_rx::db_flexrf_400_rx(usrp_basic_sptr usrp, int which) + : flexrf_base_rx(usrp, which, POWER_UP) +{ + d_common = new _400_rx(); + set_gain((gain_min() + gain_max()) / 2.0); // initialize gain +} + +db_flexrf_400_rx::~db_flexrf_400_rx() +{ +} + +float +db_flexrf_400_rx::gain_min() +{ + return usrp()->pga_min(); +} + +float +db_flexrf_400_rx::gain_max() +{ + return usrp()->pga_max()+45; +} + +float + +db_flexrf_400_rx::gain_db_per_step() +{ + return 0.035; +} + + +bool +db_flexrf_400_rx::i_and_q_swapped() +{ + return true; +} + +bool +db_flexrf_400_rx::_compute_regs(double freq, int &retR, int &retcontrol, + int &retN, double &retfreq) +{ + return d_common->_compute_regs(_refclk_freq(), freq, retR, + retcontrol, retN, retfreq); +} + diff --git a/usrp/host/lib/legacy/db_flexrf.h b/usrp/host/lib/legacy/db_flexrf.h new file mode 100644 index 000000000..b9ccfc3ac --- /dev/null +++ b/usrp/host/lib/legacy/db_flexrf.h @@ -0,0 +1,355 @@ +/* -*- 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 asversion 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 DB_FLEXRF_H +#define DB_FLEXRF_H + +#include <db_base.h> +#include <cmath> + +//debug_using_gui = true // Must be set to True or False +#define debug_using_gui false // Must be set to True or False + +class _AD4360_common; + +class flexrf_base : public db_base +{ +public: + flexrf_base(usrp_basic_sptr usrp, int which, int _power_on=0); + ~flexrf_base(); + + struct freq_result_t set_freq(double freq); + + bool is_quadrature(); + double freq_min(); + double freq_max(); + +protected: + void _write_all(int R, int control, int N); + void _write_control(int control); + void _write_R(int R); + void _write_N(int N); + void _write_it(int v); + bool _lock_detect(); + + virtual bool _compute_regs(double freq, int &retR, int &retcontrol, + int &retN, double &retfreq); + int _compute_control_reg(); + int _refclk_divisor(); + double _refclk_freq(); + + bool _set_pga(float pga_gain); + + int power_on() { return d_power_on; } + int power_off() { return 0; } + + bool d_first; + int d_spi_format; + int d_spi_enable; + int d_power_on; + int d_PD; + + _AD4360_common *d_common; +}; + +// ---------------------------------------------------------------- + +class flexrf_base_tx : public flexrf_base +{ +protected: + void shutdown(); + +public: + flexrf_base_tx(usrp_basic_sptr usrp, int which, int _power_on=0); + ~flexrf_base_tx(); + + // All RFX tx d'boards have fixed gain + float gain_min(); + float gain_max(); + float gain_db_per_step(); + + bool set_auto_tr(bool on); + bool set_enable(bool on); + bool set_gain(float gain); +}; + +class flexrf_base_rx : public flexrf_base +{ +protected: + void shutdown(); + +public: + flexrf_base_rx(usrp_basic_sptr usrp, int which, int _power_on=0); + ~flexrf_base_rx(); + + bool set_auto_tr(bool on); + bool select_rx_antenna(int which_antenna); + bool select_rx_antenna(const std::string &which_antenna); + bool set_gain(float gain); + +}; + +// ---------------------------------------------------------------- + + +class _AD4360_common +{ +public: + _AD4360_common(); + virtual ~_AD4360_common(); + + virtual double freq_min() = 0; + virtual double freq_max() = 0; + + bool _compute_regs(double refclk_freq, double freq, int &retR, + int &retcontrol, int &retN, double &retfreq); + int _compute_control_reg(); + virtual int _refclk_divisor(); + int _prescaler(); + + void R_DIV(int div) { d_R_DIV = div; } + +protected: + int d_R_RSV, d_BSC, d_TEST, d_LDP, d_ABP, d_N_RSV, d_PL, d_MTLD; + int d_CPG, d_CP3S, d_PDP, d_MUXOUT, d_CR, d_PC; + + // FIXME: d_PD might cause conflict from flexrf_base + int d_A_DIV, d_B_DIV, d_R_DIV, d_P, d_PD, d_CP2, d_CP1, d_DIVSEL; + int d_DIV2, d_CPGAIN, d_freq_mult; + +}; + +//---------------------------------------------------------------------- + +class _2400_common : public _AD4360_common +{ + public: + _2400_common(); + ~_2400_common() {} + + double freq_min(); + double freq_max(); +}; + +//---------------------------------------------------------------------- + +class _1200_common : public _AD4360_common +{ +public: + _1200_common(); + ~_1200_common() {} + + double freq_min(); + double freq_max(); +}; + +//------------------------------------------------------------------------- + +class _1800_common : public _AD4360_common +{ + public: + _1800_common(); + ~_1800_common() {} + + double freq_min(); + double freq_max(); +}; + +//------------------------------------------------------------------------- + +class _900_common : public _AD4360_common +{ +public: + _900_common(); + ~_900_common() {} + + double freq_min(); + double freq_max(); +}; + +//------------------------------------------------------------------------- + +class _400_common : public _AD4360_common +{ +public: + _400_common(); + ~_400_common() {} + + double freq_min(); + double freq_max(); +}; + +class _400_tx : public _400_common +{ +public: + _400_tx(); + ~_400_tx() {} +}; + +class _400_rx : public _400_common +{ +public: + _400_rx(); + ~_400_rx() {} +}; + +//------------------------------------------------------------ + +class db_flexrf_2400_tx : public flexrf_base_tx +{ + public: + db_flexrf_2400_tx(usrp_basic_sptr usrp, int which); + ~db_flexrf_2400_tx(); + + // Wrapper calls to d_common functions + bool _compute_regs(double freq, int &retR, int &retcontrol, + int &retN, double &retfreq); +}; + +class db_flexrf_2400_rx : public flexrf_base_rx +{ +public: + db_flexrf_2400_rx(usrp_basic_sptr usrp, int which); + ~db_flexrf_2400_rx(); + + float gain_min(); + float gain_max(); + float gain_db_per_step(); + bool i_and_q_swapped(); + + bool _compute_regs(double freq, int &retR, int &retcontrol, + int &retN, double &retfreq); +}; + +//------------------------------------------------------------ + +class db_flexrf_1200_tx : public flexrf_base_tx +{ +public: + db_flexrf_1200_tx(usrp_basic_sptr usrp, int which); + ~db_flexrf_1200_tx(); + + // Wrapper calls to d_common functions + bool _compute_regs(double freq, int &retR, int &retcontrol, + int &retN, double &retfreq); +}; + +class db_flexrf_1200_rx : public flexrf_base_rx +{ +public: + db_flexrf_1200_rx(usrp_basic_sptr usrp, int which); + ~db_flexrf_1200_rx(); + + float gain_min(); + float gain_max(); + float gain_db_per_step(); + bool i_and_q_swapped(); + + bool _compute_regs(double freq, int &retR, int &retcontrol, + int &retN, double &retfreq); +}; + +//------------------------------------------------------------ + +class db_flexrf_1800_tx : public flexrf_base_tx +{ + public: + db_flexrf_1800_tx(usrp_basic_sptr usrp, int which); + ~db_flexrf_1800_tx(); + + // Wrapper calls to d_common functions + bool _compute_regs(double freq, int &retR, int &retcontrol, + int &retN, double &retfreq); +}; + +class db_flexrf_1800_rx : public flexrf_base_rx +{ +public: + db_flexrf_1800_rx(usrp_basic_sptr usrp, int which); + ~db_flexrf_1800_rx(); + + float gain_min(); + float gain_max(); + float gain_db_per_step(); + bool i_and_q_swapped(); + + bool _compute_regs(double freq, int &retR, int &retcontrol, + int &retN, double &retfreq); +}; + +//------------------------------------------------------------ + +class db_flexrf_900_tx : public flexrf_base_tx +{ + public: + db_flexrf_900_tx(usrp_basic_sptr usrp, int which); + ~db_flexrf_900_tx(); + + // Wrapper calls to d_common functions + bool _compute_regs(double freq, int &retR, int &retcontrol, + int &retN, double &retfreq); +}; + +class db_flexrf_900_rx : public flexrf_base_rx +{ +public: + db_flexrf_900_rx(usrp_basic_sptr usrp, int which); + ~db_flexrf_900_rx(); + + float gain_min(); + float gain_max(); + float gain_db_per_step(); + bool i_and_q_swapped(); + + bool _compute_regs(double freq, int &retR, int &retcontrol, + int &retN, double &retfreq); +}; + + +//------------------------------------------------------------ + +class db_flexrf_400_tx : public flexrf_base_tx +{ + public: + db_flexrf_400_tx(usrp_basic_sptr usrp, int which); + ~db_flexrf_400_tx(); + + // Wrapper calls to d_common functions + bool _compute_regs(double freq, int &retR, int &retcontrol, + int &retN, double &retfreq); +}; + +class db_flexrf_400_rx : public flexrf_base_rx +{ +public: + db_flexrf_400_rx(usrp_basic_sptr usrp, int which); + ~db_flexrf_400_rx(); + + float gain_min(); + float gain_max(); + float gain_db_per_step(); + bool i_and_q_swapped(); + + bool _compute_regs(double freq, int &retR, int &retcontrol, + int &retN, double &retfreq); +}; + +#endif diff --git a/usrp/host/lib/legacy/db_flexrf_mimo.cc b/usrp/host/lib/legacy/db_flexrf_mimo.cc new file mode 100644 index 000000000..fd996bfa9 --- /dev/null +++ b/usrp/host/lib/legacy/db_flexrf_mimo.cc @@ -0,0 +1,276 @@ +/* + * 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 <db_flexrf_mimo.h> +#include <fpga_regs_standard.h> +#include <fpga_regs_common.h> +#include <usrp_prims.h> +#include <usrp_spi_defs.h> + + +db_flexrf_2400_tx_mimo_a::db_flexrf_2400_tx_mimo_a(usrp_basic_sptr usrp, int which) + : db_flexrf_2400_tx(usrp, which) +{ + _enable_refclk(true); + d_common->R_DIV(1); +} + +int +db_flexrf_2400_tx_mimo_a::_refclk_divisor() +{ + return 16; +} + +db_flexrf_2400_rx_mimo_a::db_flexrf_2400_rx_mimo_a(usrp_basic_sptr usrp, int which) + : db_flexrf_2400_rx(usrp, which) +{ + _enable_refclk(true); + d_common->R_DIV(1); +} + +int +db_flexrf_2400_rx_mimo_a::_refclk_divisor() +{ + return 16; +} + +db_flexrf_2400_tx_mimo_b::db_flexrf_2400_tx_mimo_b(usrp_basic_sptr usrp, int which) + : db_flexrf_2400_tx(usrp, which) +{ + d_common->R_DIV(16); +} + +int +db_flexrf_2400_tx_mimo_b::_refclk_divisor() +{ + return 1; +} + +db_flexrf_2400_rx_mimo_b::db_flexrf_2400_rx_mimo_b(usrp_basic_sptr usrp, int which) + : db_flexrf_2400_rx(usrp, which) +{ + d_common->R_DIV(16); +} + +int +db_flexrf_2400_rx_mimo_b::_refclk_divisor() +{ + return 1; +} + +db_flexrf_1800_tx_mimo_a::db_flexrf_1800_tx_mimo_a(usrp_basic_sptr usrp, int which) + : db_flexrf_1800_tx(usrp, which) +{ + _enable_refclk(true); + d_common->R_DIV(1); +} + +int +db_flexrf_1800_tx_mimo_a::_refclk_divisor() +{ + return 16; +} + +db_flexrf_1800_rx_mimo_a::db_flexrf_1800_rx_mimo_a(usrp_basic_sptr usrp, int which) + : db_flexrf_1800_rx(usrp, which) +{ + _enable_refclk(true); + d_common->R_DIV(1); +} + +int +db_flexrf_1800_rx_mimo_a::_refclk_divisor() +{ + return 16; +} + +db_flexrf_1800_tx_mimo_b::db_flexrf_1800_tx_mimo_b(usrp_basic_sptr usrp, int which) + : db_flexrf_1800_tx(usrp, which) +{ + d_common->R_DIV(16); +} + +int +db_flexrf_1800_tx_mimo_b::_refclk_divisor() +{ + return 1; +} + +db_flexrf_1800_rx_mimo_b::db_flexrf_1800_rx_mimo_b(usrp_basic_sptr usrp, int which) + : db_flexrf_1800_rx(usrp, which) +{ + d_common->R_DIV(16); +} + +int +db_flexrf_1800_rx_mimo_b::_refclk_divisor() +{ + return 1; +} + +db_flexrf_1200_tx_mimo_a::db_flexrf_1200_tx_mimo_a(usrp_basic_sptr usrp, int which) + : db_flexrf_1200_tx(usrp, which) +{ + _enable_refclk(true); + d_common->R_DIV(1); +} + +int +db_flexrf_1200_tx_mimo_a::_refclk_divisor() +{ + return 16; +} + +db_flexrf_1200_rx_mimo_a::db_flexrf_1200_rx_mimo_a(usrp_basic_sptr usrp, int which) + : db_flexrf_1200_rx(usrp, which) +{ + _enable_refclk(true); + d_common->R_DIV(1); +} + +int +db_flexrf_1200_rx_mimo_a::_refclk_divisor() +{ + return 16; +} + +db_flexrf_1200_tx_mimo_b::db_flexrf_1200_tx_mimo_b(usrp_basic_sptr usrp, int which) + : db_flexrf_1200_tx(usrp, which) +{ + d_common->R_DIV(16); +} + +int +db_flexrf_1200_tx_mimo_b::_refclk_divisor() +{ + return 1; +} + +db_flexrf_1200_rx_mimo_b::db_flexrf_1200_rx_mimo_b(usrp_basic_sptr usrp, int which) + : db_flexrf_1200_rx(usrp, which) +{ + d_common->R_DIV(16); +} + +int +db_flexrf_1200_rx_mimo_b::_refclk_divisor() +{ + return 1; +} + +db_flexrf_900_tx_mimo_a::db_flexrf_900_tx_mimo_a(usrp_basic_sptr usrp, int which) + : db_flexrf_900_tx(usrp, which) +{ + _enable_refclk(true); + d_common->R_DIV(1); +} + +int +db_flexrf_900_tx_mimo_a::_refclk_divisor() +{ + return 16; +} + +db_flexrf_900_rx_mimo_a::db_flexrf_900_rx_mimo_a(usrp_basic_sptr usrp, int which) + : db_flexrf_900_rx(usrp, which) +{ + _enable_refclk(true); + d_common->R_DIV(1); +} + +int +db_flexrf_900_rx_mimo_a::_refclk_divisor() +{ + return 16; +} + +db_flexrf_900_tx_mimo_b::db_flexrf_900_tx_mimo_b(usrp_basic_sptr usrp, int which) + : db_flexrf_900_tx(usrp, which) +{ + d_common->R_DIV(16); +} + +int +db_flexrf_900_tx_mimo_b::_refclk_divisor() +{ + return 1; +} + +db_flexrf_900_rx_mimo_b::db_flexrf_900_rx_mimo_b(usrp_basic_sptr usrp, int which) + : db_flexrf_900_rx(usrp, which) +{ + d_common->R_DIV(16); +} + +int db_flexrf_900_rx_mimo_b::_refclk_divisor() +{ + return 1; +} + +db_flexrf_400_tx_mimo_a::db_flexrf_400_tx_mimo_a(usrp_basic_sptr usrp, int which) + : db_flexrf_400_tx(usrp, which) +{ + _enable_refclk(true); + d_common->R_DIV(1); +} + +int +db_flexrf_400_tx_mimo_a::_refclk_divisor() +{ + return 16; +} + +db_flexrf_400_rx_mimo_a::db_flexrf_400_rx_mimo_a(usrp_basic_sptr usrp, int which) + : db_flexrf_400_rx(usrp, which) +{ + _enable_refclk(true); + d_common->R_DIV(1); +} + +int +db_flexrf_400_rx_mimo_a::_refclk_divisor() +{ + return 16; +} + +db_flexrf_400_tx_mimo_b::db_flexrf_400_tx_mimo_b(usrp_basic_sptr usrp, int which) + : db_flexrf_400_tx(usrp, which) +{ + d_common->R_DIV(16); +} + +int +db_flexrf_400_tx_mimo_b::_refclk_divisor() +{ + return 1; +} + +db_flexrf_400_rx_mimo_b::db_flexrf_400_rx_mimo_b(usrp_basic_sptr usrp, int which) + : db_flexrf_400_rx(usrp, which) +{ + d_common->R_DIV(16); +} + +int +db_flexrf_400_rx_mimo_b::_refclk_divisor() +{ + return 1; +} diff --git a/usrp/host/lib/legacy/db_flexrf_mimo.h b/usrp/host/lib/legacy/db_flexrf_mimo.h new file mode 100644 index 000000000..aeff53258 --- /dev/null +++ b/usrp/host/lib/legacy/db_flexrf_mimo.h @@ -0,0 +1,163 @@ +/* + * 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 <db_flexrf.h> + +class db_flexrf_2400_tx_mimo_a : public db_flexrf_2400_tx +{ + public: + db_flexrf_2400_tx_mimo_a(usrp_basic_sptr usrp, int which); + int _refclk_divisor(); +}; + +class db_flexrf_2400_rx_mimo_a : public db_flexrf_2400_rx +{ + public: + db_flexrf_2400_rx_mimo_a(usrp_basic_sptr usrp, int which); + int _refclk_divisor(); +}; + +class db_flexrf_2400_tx_mimo_b : public db_flexrf_2400_tx +{ + public: + db_flexrf_2400_tx_mimo_b(usrp_basic_sptr usrp, int which); + int _refclk_divisor(); +}; + +class db_flexrf_2400_rx_mimo_b : public db_flexrf_2400_rx +{ + public: + db_flexrf_2400_rx_mimo_b(usrp_basic_sptr usrp, int which); + int _refclk_divisor(); +}; + + +class db_flexrf_1800_tx_mimo_a : public db_flexrf_1800_tx +{ + public: + db_flexrf_1800_tx_mimo_a(usrp_basic_sptr usrp, int which); + int _refclk_divisor(); +}; + +class db_flexrf_1800_rx_mimo_a : public db_flexrf_1800_rx +{ + public: + db_flexrf_1800_rx_mimo_a(usrp_basic_sptr usrp, int which); + int _refclk_divisor(); +}; + +class db_flexrf_1800_tx_mimo_b : public db_flexrf_1800_tx +{ + public: + db_flexrf_1800_tx_mimo_b(usrp_basic_sptr usrp, int which); + int _refclk_divisor(); +}; + +class db_flexrf_1800_rx_mimo_b : public db_flexrf_1800_rx +{ + public: + db_flexrf_1800_rx_mimo_b(usrp_basic_sptr usrp, int which); + int _refclk_divisor(); +}; + +class db_flexrf_1200_tx_mimo_a : public db_flexrf_1200_tx +{ + public: + db_flexrf_1200_tx_mimo_a(usrp_basic_sptr usrp, int which); + int _refclk_divisor(); +}; + +class db_flexrf_1200_rx_mimo_a : public db_flexrf_1200_rx +{ + public: + db_flexrf_1200_rx_mimo_a(usrp_basic_sptr usrp, int which); + int _refclk_divisor(); +}; + +class db_flexrf_1200_tx_mimo_b : public db_flexrf_1200_tx +{ + public: + db_flexrf_1200_tx_mimo_b(usrp_basic_sptr usrp, int which); + int _refclk_divisor(); +}; + +class db_flexrf_1200_rx_mimo_b : public db_flexrf_1200_rx +{ + public: + db_flexrf_1200_rx_mimo_b(usrp_basic_sptr usrp, int which); + int _refclk_divisor(); +}; + +class db_flexrf_900_tx_mimo_a : public db_flexrf_900_tx +{ + public: + db_flexrf_900_tx_mimo_a(usrp_basic_sptr usrp, int which); + int _refclk_divisor(); +}; + +class db_flexrf_900_rx_mimo_a : public db_flexrf_900_rx +{ + public: + db_flexrf_900_rx_mimo_a(usrp_basic_sptr usrp, int which); + int _refclk_divisor(); +}; + +class db_flexrf_900_tx_mimo_b : public db_flexrf_900_tx +{ + public: + db_flexrf_900_tx_mimo_b(usrp_basic_sptr usrp, int which); + int _refclk_divisor(); +}; + +class db_flexrf_900_rx_mimo_b : public db_flexrf_900_rx +{ + public: + db_flexrf_900_rx_mimo_b(usrp_basic_sptr usrp, int which); + int _refclk_divisor(); +}; + +class db_flexrf_400_tx_mimo_a : public db_flexrf_400_tx +{ + public: + db_flexrf_400_tx_mimo_a(usrp_basic_sptr usrp, int which); + int _refclk_divisor(); +}; + +class db_flexrf_400_rx_mimo_a : public db_flexrf_400_rx +{ + public: + db_flexrf_400_rx_mimo_a(usrp_basic_sptr usrp, int which); + int _refclk_divisor(); +}; + +class db_flexrf_400_tx_mimo_b : public db_flexrf_400_tx +{ + public: + db_flexrf_400_tx_mimo_b(usrp_basic_sptr usrp, int which); + int _refclk_divisor(); +}; + +class db_flexrf_400_rx_mimo_b : public db_flexrf_400_rx +{ + public: + db_flexrf_400_rx_mimo_b(usrp_basic_sptr usrp, int which); + int _refclk_divisor(); +}; diff --git a/usrp/host/lib/legacy/db_tv_rx.cc b/usrp/host/lib/legacy/db_tv_rx.cc new file mode 100644 index 000000000..803ebf86d --- /dev/null +++ b/usrp/host/lib/legacy/db_tv_rx.cc @@ -0,0 +1,274 @@ +// +// 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 asversion 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 <db_tv_rx.h> +#include <db_base_impl.h> + +/*****************************************************************************/ + +int +control_byte_1(bool fast_tuning_p, int reference_divisor) +{ + int c = 0x88; + if(fast_tuning_p) { + c |= 0x40; + } + + if(reference_divisor == 512) { + c |= 0x3 << 1; + } + else if(reference_divisor == 640) { + c |= 0x0 << 1; + } + else if(reference_divisor == 1024) { + c |= 0x1 << 1; + } + else { + assert(0); + } + + return c; +} + +int +control_byte_2(double target_freq, bool shutdown_tx_PGA) +{ + int c; + if(target_freq < 158e6) { // VHF low + c = 0xa0; + } + else if(target_freq < 464e6) { // VHF high + c = 0x90; + } + else { // UHF + c = 0x30; + } + + if(shutdown_tx_PGA) { + c |= 0x08; + } + + return c; +} + + +/*****************************************************************************/ + + +db_tv_rx::db_tv_rx(usrp_basic_sptr usrp, int which, + double first_IF, double second_IF) + : db_base(usrp, which) +{ + // Handler for Tv Rx daughterboards. + // + // @param usrp: instance of usrp.source_c + // @param which: which side: 0, 1 corresponding to RX_A or RX_B respectively + + if(which == 0) { + d_i2c_addr = 0x60; + } + else { + d_i2c_addr = 0x61; + } + + d_first_IF = first_IF; + d_second_IF = second_IF; + d_reference_divisor = 640; + d_fast_tuning = false; + d_inverted = false; // FIXME get rid of this + + set_gain((gain_min() + gain_max()) / 2.0); // initialize gain + + bypass_adc_buffers(false); +} + +db_tv_rx::~db_tv_rx() +{ +} + +// Gain setting +void +db_tv_rx::_set_rfagc(float gain) +{ + float voltage; + + assert(gain <= 60 && gain >= 0); + // FIXME this has a 0.5V step between gain = 60 and gain = 59. + // Why are there two cases instead of a single linear case? + if(gain == 60) { + voltage = 4; + } + else { + voltage = gain/60.0 * 2.25 + 1.25; + } + int dacword = int(4096*voltage/1.22/3.3); // 1.22 = opamp gain + + assert(dacword>=0 && dacword<4096); + usrp()->write_aux_dac(d_which, 1, dacword); +} + +void +db_tv_rx::_set_ifagc(float gain) +{ + float voltage; + + assert(gain <= 35 && gain >= 0); + voltage = gain/35.0 * 2.1 + 1.4; + int dacword = int(4096*voltage/1.22/3.3); // 1.22 = opamp gain + + assert(dacword>=0 && dacword<4096); + usrp()->write_aux_dac(d_which, 0, dacword); +} + +void +db_tv_rx::_set_pga(float pga_gain) +{ + assert(pga_gain >=0 && pga_gain <=20); + if(d_which == 0) { + usrp()->set_pga(0, pga_gain); + } + else { + usrp()->set_pga (2, pga_gain); + } +} + +double +db_tv_rx::freq_min() +{ + return 50e6; +} + +double +db_tv_rx::freq_max() +{ + return 860e6; +} + +struct freq_result_t +db_tv_rx::set_freq(double target_freq) +{ + // Set the frequency. + // + // @param freq: target RF frequency in Hz + // @type freq: double + // + // @returns (ok, actual_baseband_freq) where: + // ok is True or False and indicates success or failure, + // actual_baseband_freq is RF frequency that corresponds to DC in the IF. + + freq_result_t args = {false, 0}; + + double fmin = freq_min(); + double fmax = freq_max(); + if((target_freq < fmin) || (target_freq > fmax)) { + return args; + } + + double target_lo_freq = target_freq + d_first_IF; // High side mixing + double f_ref = 4.0e6 / (double)(d_reference_divisor); // frequency steps + + int divisor = int((target_lo_freq + (f_ref * 4)) / (f_ref * 8)); + double actual_lo_freq = (f_ref * 8 * divisor); + double actual_freq = actual_lo_freq - d_first_IF; + + if((divisor & ~0x7fff) != 0) { // must be 15-bits or less + return args; + } + + // build i2c command string + std::vector<int> buf(4); + buf[0] = (divisor >> 8) & 0xff; // DB1 + buf[1] = divisor & 0xff; // DB2 + buf[2] = control_byte_1(d_fast_tuning, d_reference_divisor); + buf[3] = control_byte_2(actual_freq, true); + + args.ok = usrp()->write_i2c(d_i2c_addr, int_seq_to_str (buf)); + args.baseband_freq = actual_freq - d_second_IF; + return args; +} + +float +db_tv_rx::gain_min() +{ + return 0; +} + +float +db_tv_rx::gain_max() +{ + return 115; +} + +float +db_tv_rx::gain_db_per_step() +{ + return 1; +} + +bool +db_tv_rx::set_gain(float gain) +{ + // Set the gain. + // + // @param gain: gain in decibels + // @returns True/False + + float rfgain, ifgain, pgagain; + + assert(gain>=0 && gain<=115); + if(gain>60) { + rfgain = 60; + gain = gain - 60; + } + else { + rfgain = gain; + gain = 0; + } + + if(gain > 35) { + ifgain = 35; + gain = gain - 35; + } + else { + ifgain = gain; + gain = 0; + } + + pgagain = gain; + _set_rfagc(rfgain); + _set_ifagc(ifgain); + _set_pga(pgagain); + + return true; +} + +bool +db_tv_rx::is_quadrature() +{ + // Return True if this board requires both I & Q analog channels. + return false; +} + +bool +db_tv_rx::spectrum_inverted() +{ + // The 43.75 MHz version is inverted + return d_inverted; +} diff --git a/usrp/host/lib/legacy/db_tv_rx.h b/usrp/host/lib/legacy/db_tv_rx.h new file mode 100644 index 000000000..ed9162637 --- /dev/null +++ b/usrp/host/lib/legacy/db_tv_rx.h @@ -0,0 +1,56 @@ +/* -*- 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 asversion 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 DB_TV_RX_H +#define DB_TV_RX_H + +#include <db_base.h> + +class db_tv_rx : public db_base +{ +private: + void _set_rfagc(float gain); + void _set_ifagc(float gain); + void _set_pga(float pga_gain); + + int d_i2c_addr; + double d_first_IF, d_second_IF; + int d_reference_divisor; + bool d_fast_tuning; + bool d_inverted; + +public: + db_tv_rx(usrp_basic_sptr usrp, int which, + double first_IF, double second_IF); + ~db_tv_rx(); + + float gain_min(); + float gain_max(); + float gain_db_per_step(); + double freq_min(); + double freq_max(); + struct freq_result_t set_freq(double target_freq); + bool set_gain(float gain); + bool is_quadrature(); + bool spectrum_inverted(); +}; + +#endif diff --git a/usrp/host/lib/legacy/db_util.cc b/usrp/host/lib/legacy/db_util.cc new file mode 100644 index 000000000..4b46383bb --- /dev/null +++ b/usrp/host/lib/legacy/db_util.cc @@ -0,0 +1,54 @@ +/* -*- 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 this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif +#include <db_util.h> +#include <sstream> + +std::string +int_seq_to_str(std::vector<int> &seq) +{ + //convert a sequence of integers into a string + + std::stringstream str; + std::vector<int>::iterator i; + for(i = seq.begin(); i != seq.end(); i++) { + str << char((unsigned int)*i); + } + return str.str(); +} + +std::vector<int> +str_to_int_seq(std::string str) +{ + //convert a string to a list of integers + std::vector<int> seq; + std::vector<int>::iterator sitr; + std::string::iterator i; + for(i=str.begin(); i != str.end(); i++) { + int a = (int)(*i); + seq.push_back(a); + } + return seq; +} + diff --git a/usrp/host/lib/legacy/db_util.h b/usrp/host/lib/legacy/db_util.h new file mode 100644 index 000000000..e07abb608 --- /dev/null +++ b/usrp/host/lib/legacy/db_util.h @@ -0,0 +1,31 @@ +/* -*- 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 this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef INCLUDED_DB_UTIL_H +#define INCLUDED_DB_UTIL_H + +#include <string> +#include <vector> + +std::string int_seq_to_str(std::vector<int> &seq); +std::vector<int> str_to_int_seq(std::string str); + +#endif /* INCLUDED_DB_UTIL_H */ diff --git a/usrp/host/lib/legacy/db_wbx.cc b/usrp/host/lib/legacy/db_wbx.cc new file mode 100644 index 000000000..9f1d72939 --- /dev/null +++ b/usrp/host/lib/legacy/db_wbx.cc @@ -0,0 +1,953 @@ +/* -*- 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 asversion 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 <db_wbx.h> +#include <fpga_regs_standard.h> +#include <fpga_regs_common.h> +#include <usrp_prims.h> +#include <usrp_spi_defs.h> +#include <stdexcept> +#include <cmath> + +// d'board i/o pin defs + +// TX IO Pins +#define TX_POWER (1 << 0) // TX Side Power +#define RX_TXN (1 << 1) // T/R antenna switch for TX/RX port +#define TX_ENB_MIX (1 << 2) // Enable IQ mixer +#define TX_ENB_VGA (1 << 3) + +// RX IO Pins +#define RX2_RX1N (1 << 0) // antenna switch between RX2 and TX/RX port +#define RXENABLE (1 << 1) // enables mixer +#define PLL_LOCK_DETECT (1 << 2) // Muxout pin from PLL -- MUST BE INPUT +#define MReset (1 << 3) // NB6L239 Master Reset, asserted low +#define SELA0 (1 << 4) // NB6L239 SelA0 +#define SELA1 (1 << 5) // NB6L239 SelA1 +#define SELB0 (1 << 6) // NB6L239 SelB0 +#define SELB1 (1 << 7) // NB6L239 SelB1 +#define PLL_ENABLE (1 << 8) // CE Pin on PLL +#define AUX_SCLK (1 << 9) // ALT SPI SCLK +#define AUX_SDO (1 << 10) // ALT SPI SDO +#define AUX_SEN (1 << 11) // ALT SPI SEN + + +wbx_base::wbx_base(usrp_basic_sptr usrp, int which) + : db_base(usrp, which) +{ + /* + * @param usrp: instance of usrp.source_c + * @param which: which side: 0 or 1 corresponding to side A or B respectively + * @type which: int + */ + + d_first = true; + d_spi_format = SPI_FMT_MSB | SPI_FMT_HDR_0; + + // FIXME -- the write reg functions don't work with 0xffff for masks + _rx_write_oe(int(PLL_ENABLE|MReset|SELA0|SELA1|SELB0|SELB1|RX2_RX1N|RXENABLE), 0x7fff); + _rx_write_io((PLL_ENABLE|MReset|0|RXENABLE), (PLL_ENABLE|MReset|RX2_RX1N|RXENABLE)); + + _tx_write_oe((TX_POWER|RX_TXN|TX_ENB_MIX|TX_ENB_VGA), 0x7fff); + _tx_write_io((0|RX_TXN), (TX_POWER|RX_TXN|TX_ENB_MIX|TX_ENB_VGA)); // TX off, TR switch set to RX + + if(d_which == 0) { + d_spi_enable = SPI_ENABLE_RX_A; + } + else { + d_spi_enable = SPI_ENABLE_RX_B; + } + + set_auto_tr(false); + +} + +wbx_base::~wbx_base() +{ + shutdown(); +} + + +void +wbx_base::shutdown() +{ + if (!d_is_shutdown){ + d_is_shutdown = true; + // do whatever there is to do to shutdown + + write_io(d_which, d_power_off, POWER_UP); // turn off power to board + _write_oe(d_which, 0, 0xffff); // turn off all outputs + set_auto_tr(false); // disable auto transmit + } +} + +bool +wbx_base::_lock_detect() +{ + /* + * @returns: the value of the VCO/PLL lock detect bit. + * @rtype: 0 or 1 + */ + + if(_rx_read_io() & PLL_LOCK_DETECT) { + return true; + } + else { // Give it a second chance + if(_rx_read_io() & PLL_LOCK_DETECT) { + return true; + } + else { + return false; + } + } +} + +bool +wbx_base::_tx_write_oe(int value, int mask) +{ + int reg = (d_which == 0 ? FR_OE_0 : FR_OE_2); + return d_usrp->_write_fpga_reg(reg, ((mask & 0xffff) << 16) | (value & 0xffff)); +} + +bool +wbx_base::_rx_write_oe(int value, int mask) +{ + int reg = (d_which == 0 ? FR_OE_1 : FR_OE_3); + return d_usrp->_write_fpga_reg(reg, ((mask & 0xffff) << 16) | (value & 0xffff)); +} + +bool +wbx_base::_tx_write_io(int value, int mask) +{ + int reg = (d_which == 0 ? FR_IO_0 : FR_IO_2); + return d_usrp->_write_fpga_reg(reg, ((mask & 0xffff) << 16) | (value & 0xffff)); +} + +bool +wbx_base::_rx_write_io(int value, int mask) +{ + int reg = (d_which == 0 ? FR_IO_1 : FR_IO_3); + return d_usrp->_write_fpga_reg(reg, ((mask & 0xffff) << 16) | (value & 0xffff)); +} + +bool +wbx_base::_rx_read_io() +{ + int reg = (d_which == 0 ? FR_RB_IO_RX_A_IO_TX_A : FR_RB_IO_RX_B_IO_TX_B); + int t = d_usrp->_read_fpga_reg(reg); + return (t >> 16) & 0xffff; +} + +bool +wbx_base::_tx_read_io() +{ + int reg = (d_which == 0 ? FR_RB_IO_RX_A_IO_TX_A : FR_RB_IO_RX_B_IO_TX_B); + int t = d_usrp->_read_fpga_reg(reg); + return (t & 0xffff); +} + +bool +wbx_base::_compute_regs(double freq) +{ + /* + * Determine values of registers, along with actual freq. + * + * @param freq: target frequency in Hz + * @type freq: double + * @returns: (R, N, func, init, actual_freq) + * @rtype: tuple(int, int, int, int, double) + * + * Override this in derived classes. + */ + throw std::runtime_error("_compute_regs called from base class\n"); +} + +double +wbx_base::_refclk_freq() +{ + return (double)(d_usrp->fpga_master_clock_freq())/_refclk_divisor(); +} + +int +wbx_base::_refclk_divisor() +{ + /* + * Return value to stick in REFCLK_DIVISOR register + */ + return 1; +} + +struct freq_result_t +wbx_base::set_freq(double freq) +{ + /* + * @returns (ok, actual_baseband_freq) where: + * ok is True or False and indicates success or failure, + * actual_baseband_freq is the RF frequency that corresponds to DC in the IF. + */ + throw std::runtime_error("set_freq called from base class\n"); +} + +float +wbx_base::gain_min() +{ + throw std::runtime_error("gain_min called from base class\n"); +} + +float +wbx_base::gain_max() +{ + throw std::runtime_error("gain_max called from base class\n"); +} + +float +wbx_base::gain_db_per_step() +{ + throw std::runtime_error("gain_db_per_step called from base class\n"); +} + +bool +wbx_base::set_gain(float gain) +{ + /* + * Set the gain. + * + * @param gain: gain in decibels + * @returns True/False + */ + throw std::runtime_error("set_gain called from base class\n"); +} + +bool +wbx_base::_set_pga(float pga_gain) +{ + bool ok; + if(d_which == 0) { + ok = d_usrp->set_pga(0, pga_gain); + ok |= d_usrp->set_pga(1, pga_gain); + } + else { + ok = d_usrp->set_pga(2, pga_gain); + ok |= d_usrp->set_pga(3, pga_gain); + } + return ok; +} + +bool +wbx_base::is_quadrature() +{ + /* + * Return True if this board requires both I & Q analog channels. + * + * This bit of info is useful when setting up the USRP Rx mux register. + */ + return true; +} + + +/****************************************************************************/ + + +wbx_base_tx::wbx_base_tx(usrp_basic_sptr usrp, int which) + : wbx_base(usrp, which) +{ + /* + * @param usrp: instance of usrp.sink_c + * @param which: 0 or 1 corresponding to side TX_A or TX_B respectively. + */ + + // power up the transmit side, NO -- but set antenna to receive + d_usrp->write_io(d_which, (TX_POWER), (TX_POWER|RX_TXN)); + d_lo_offset = 0e6; + + // Gain is not set by the PGA, but the PGA must be set at max gain in the TX + _set_pga(d_usrp->pga_max()); +} + +wbx_base_tx::~wbx_base_tx() +{ + // Power down and leave the T/R switch in the R position + d_usrp->write_io(d_which, (RX_TXN), (TX_POWER|RX_TXN|TX_ENB_MIX|TX_ENB_VGA)); +} + +void +wbx_base_tx::set_auto_tr(bool on) +{ + if(on) { + set_atr_mask (RX_TXN); + set_atr_txval(0); + set_atr_rxval(RX_TXN); + } + else { + set_atr_mask (0); + set_atr_txval(0); + set_atr_rxval(0); + } +} + +void +wbx_base_tx::set_enable(bool on) +{ + /* + * Enable transmitter if on is True + */ + + int mask = RX_TXN|TX_ENB_MIX|TX_ENB_VGA; + //printf("HERE!!!!\n"); + if(on) { + d_usrp->write_io(d_which, TX_ENB_MIX|TX_ENB_VGA, mask); + } + else { + d_usrp->write_io(d_which, RX_TXN, mask); + } +} + +void +wbx_base_tx::set_lo_offset(double offset) +{ + /* + * Set amount by which LO is offset from requested tuning frequency. + * + * @param offset: offset in Hz + */ + + d_lo_offset = offset; +} + +double +wbx_base_tx::lo_offset() +{ + /* + * Get amount by which LO is offset from requested tuning frequency. + * + * @returns Offset in Hz + */ + + return d_lo_offset; +} + + +/****************************************************************************/ + + +wbx_base_rx::wbx_base_rx(usrp_basic_sptr usrp, int which) + : wbx_base(usrp, which) +{ + /* + * @param usrp: instance of usrp.source_c + * @param which: 0 or 1 corresponding to side RX_A or RX_B respectively. + */ + + // set up for RX on TX/RX port + select_rx_antenna("TX/RX"); + + bypass_adc_buffers(true); + + d_lo_offset = 0.0; +} + +wbx_base_rx::~wbx_base_rx() +{ + // Power down + d_usrp->write_io(d_which, 0, (RXENABLE)); +} + +void +wbx_base_rx::set_auto_tr(bool on) +{ + if(on) { + // FIXME: where does ENABLE come from? + //set_atr_mask (ENABLE); + set_atr_txval( 0); + //set_atr_rxval(ENABLE); + } + else { + set_atr_mask (0); + set_atr_txval(0); + set_atr_rxval(0); + } +} + +void +wbx_base_rx::select_rx_antenna(int which_antenna) +{ + /* + * Specify which antenna port to use for reception. + * @param which_antenna: either 'TX/RX' or 'RX2' + */ + + if(which_antenna == 0) { + d_usrp->write_io(d_which, 0, RX2_RX1N); + } + else if(which_antenna == 1) { + d_usrp->write_io(d_which, RX2_RX1N, RX2_RX1N); + } + else { + throw std::invalid_argument("which_antenna must be either 'TX/RX' or 'RX2'\n"); + } +} + +void +wbx_base_rx::select_rx_antenna(const std::string &which_antenna) +{ + if(which_antenna == "TX/RX") { + select_rx_antenna(0); + } + else if(which_antenna == "RX2") { + select_rx_antenna(1); + } + else { + throw std::invalid_argument("which_antenna must be either 'TX/RX' or 'RX2'\n"); + } +} + +bool +wbx_base_rx::set_gain(float gain) +{ + /* + * Set the gain. + * + * @param gain: gain in decibels + * @returns True/False + */ + + float pga_gain, agc_gain; + float maxgain = gain_max() - d_usrp->pga_max(); + float mingain = gain_min(); + if(gain > maxgain) { + pga_gain = gain-maxgain; + assert(pga_gain <= d_usrp->pga_max()); + agc_gain = maxgain; + } + else { + pga_gain = 0; + agc_gain = gain; + } + + float V_maxgain = .2; + float V_mingain = 1.2; + float V_fullscale = 3.3; + float dac_value = (agc_gain*(V_maxgain-V_mingain)/(maxgain-mingain) + V_mingain)*4096/V_fullscale; + + assert(dac_value>=0 && dac_value<4096); + + return d_usrp->write_aux_dac(d_which, 0, (int)(dac_value)) && _set_pga((int)(pga_gain)); +} + +void +wbx_base_rx::set_lo_offset(double offset) +{ + /* + * Set amount by which LO is offset from requested tuning frequency. + * + * @param offset: offset in Hz + */ + d_lo_offset = offset; +} + +double +wbx_base_rx::lo_offset() +{ + /* + * Get amount by which LO is offset from requested tuning frequency. + * + * @returns Offset in Hz + */ + return d_lo_offset; +} + +bool +wbx_base_rx::i_and_q_swapped() +{ + /* + * Return True if this is a quadrature device and ADC 0 is Q. + */ + return false; +} + + +/****************************************************************************/ + +_ADF410X_common::_ADF410X_common() +{ + // R-Register Common Values + d_R_RSV = 0; // bits 23,22,21 + d_LDP = 1; // bit 20 Lock detect in 5 cycles + d_TEST = 0; // bit 19,18 Normal + d_ABP = 0; // bit 17,16 2.9ns + + // N-Register Common Values + d_N_RSV = 0; // 23,22 + d_CP_GAIN = 0; // 21 + + // Function Register Common Values + d_P = 0; // bits 23,22 0 = 8/9, 1 = 16/17, 2 = 32/33, 3 = 64/65 + d_PD2 = 0; // bit 21 Normal operation + d_CP2 = 4; // bits 20,19,18 CP Gain = 5mA + d_CP1 = 4; // bits 17,16,15 CP Gain = 5mA + d_TC = 0; // bits 14-11 PFD Timeout + d_FL = 0; // bit 10,9 Fastlock Disabled + d_CP3S = 0; // bit 8 CP Enabled + d_PDP = 0; // bit 7 Phase detector polarity, Positive=1 + d_MUXOUT = 1; // bits 6:4 Digital Lock Detect + d_PD1 = 0; // bit 3 Normal operation + d_CR = 0; // bit 2 Normal operation +} + +_ADF410X_common::~_ADF410X_common() +{ +} + +bool +_ADF410X_common::_compute_regs(double freq, int &retR, int &retcontrol, + int &retN, double &retfreq) +{ + /* + * Determine values of R, control, and N registers, along with actual freq. + * + * @param freq: target frequency in Hz + * @type freq: double + * @returns: (R, N, control, actual_freq) + * @rtype: tuple(int, int, int, double) + */ + + // Band-specific N-Register Values + double phdet_freq = _refclk_freq()/d_R_DIV; + printf("phdet_freq = %f\n", phdet_freq); + + double desired_n = round(freq*d_freq_mult/phdet_freq); + printf("desired_n %f\n", desired_n); + + double actual_freq = desired_n * phdet_freq; + printf("actual freq %f\n", actual_freq); + + double B = floor(desired_n/_prescaler()); + double A = desired_n - _prescaler()*B; + printf("A %f B %f\n", A, B); + + d_B_DIV = int(B); // bits 20:8; + d_A_DIV = int(A); // bit 6:2; + + if(d_B_DIV < d_A_DIV) { + retR = 0; + retN = 0; + retcontrol = 0; + retfreq = 0; + return false; + } + + retR = (d_R_RSV<<21) | (d_LDP<<20) | (d_TEST<<18) | + (d_ABP<<16) | (d_R_DIV<<2); + + retN = (d_N_RSV<<22) | (d_CP_GAIN<<21) | (d_B_DIV<<8) | (d_A_DIV<<2); + + retcontrol = (d_P<<22) | (d_PD2<<21) | (d_CP2<<18) | (d_CP1<<15) | + (d_TC<<11) | (d_FL<<9) | (d_CP3S<<8) | (d_PDP<<7) | + (d_MUXOUT<<4) | (d_PD1<<3) | (d_CR<<2); + + retfreq = actual_freq/d_freq_mult; + + return true; +} + +void +_ADF410X_common::_write_all(int R, int N, int control) +{ + /* + * Write all PLL registers: + * R counter latch, + * N counter latch, + * Function latch, + * Initialization latch + * + * Adds 10ms delay between writing control and N if this is first call. + * This is the required power-up sequence. + * + * @param R: 24-bit R counter latch + * @type R: int + * @param N: 24-bit N counter latch + * @type N: int + * @param control: 24-bit control latch + * @type control: int + */ + static bool first = true; + + timespec t; + t.tv_sec = 0; + t.tv_nsec = 10000000; + + _write_R(R); + _write_func(control); + _write_init(control); + if(first) { + //time.sleep(0.010); + nanosleep(&t, NULL); + first = false; + } + _write_N(N); +} + +void +_ADF410X_common::_write_R(int R) +{ + _write_it((R & ~0x3) | 0); +} + +void +_ADF410X_common::_write_N(int N) +{ + _write_it((N & ~0x3) | 1); +} + +void +_ADF410X_common::_write_func(int func) +{ + _write_it((func & ~0x3) | 2); +} + +void +_ADF410X_common::_write_init(int init) +{ + _write_it((init & ~0x3) | 3); +} + +void +_ADF410X_common::_write_it(int v) +{ + char c[3]; + c[0] = (char)((v >> 16) & 0xff); + c[1] = (char)((v >> 8) & 0xff); + c[2] = (char)((v & 0xff)); + std::string s(c, 3); + //d_usrp->_write_spi(0, d_spi_enable, d_spi_format, s); + usrp()->_write_spi(0, d_spi_enable, d_spi_format, s); +} + +int +_ADF410X_common::_prescaler() +{ + if(d_P == 0) { + return 8; + } + else if(d_P == 1) { + return 16; + } + else if(d_P == 2) { + return 32; + } + else if(d_P == 3) { + return 64; + } + else { + throw std::invalid_argument("Prescaler out of range\n"); + } +} + +double +_ADF410X_common::_refclk_freq() +{ + throw std::runtime_error("_refclk_freq called from base class."); +} + +bool +_ADF410X_common::_rx_write_io(int value, int mask) +{ + throw std::runtime_error("_rx_write_io called from base class."); +} + +bool +_ADF410X_common::_lock_detect() +{ + throw std::runtime_error("_lock_detect called from base class."); +} + +usrp_basic* +_ADF410X_common::usrp() +{ + throw std::runtime_error("usrp() called from base class."); +} + + +/****************************************************************************/ + + +_lo_common::_lo_common() + : _ADF410X_common() +{ + // Band-specific R-Register Values + d_R_DIV = 4; // bits 15:2 + + // Band-specific C-Register values + d_P = 0; // bits 23,22 0 = Div by 8/9 + d_CP2 = 4; // bits 19:17 + d_CP1 = 4; // bits 16:14 + + // Band specifc N-Register Values + d_DIVSEL = 0; // bit 23 + d_DIV2 = 0; // bit 22 + d_CPGAIN = 0; // bit 21 + d_freq_mult = 1; + + d_div = 1; + d_aux_div = 2; + d_main_div = 0; +} + +_lo_common::~_lo_common() +{ +} + +double +_lo_common::freq_min() +{ + return 50e6; +} + +double +_lo_common::freq_max() +{ + return 1000e6; +} + +void +_lo_common::set_divider(int main_or_aux, int divisor) +{ + if(main_or_aux == 0) { + if((divisor != 1) || (divisor != 2) || (divisor != 4) || (divisor != 8)) { + throw std::invalid_argument("Main Divider Must be 1, 2, 4, or 8\n"); + } + d_main_div = (int)(log10(divisor)/log10(2.0)); + } + else if(main_or_aux == 1) { + if((divisor != 2) || (divisor != 4) || (divisor != 8) || (divisor != 16)) { + throw std::invalid_argument("Aux Divider Must be 2, 4, 8 or 16\n"); + } + d_main_div = (int)(log10(divisor/2.0)/log10(2.0)); + } + else { + throw std::invalid_argument("main_or_aux must be 'main' or 'aux'\n"); + } + + int vala = d_main_div*SELA0; + int valb = d_aux_div*SELB0; + int mask = SELA0|SELA1|SELB0|SELB1; + + _rx_write_io((vala | valb), mask); +} + +void +_lo_common::set_divider(const std::string &main_or_aux, int divisor) +{ + if(main_or_aux == "main") { + set_divider(0, divisor); + } + else if(main_or_aux == "aux") { + set_divider(1, divisor); + } + else { + throw std::invalid_argument("main_or_aux must be 'main' or 'aux'\n"); + } +} + +struct freq_result_t +_lo_common::set_freq(double freq) +{ + struct freq_result_t ret; + + if(freq < 20e6 or freq > 1200e6) { + throw std::invalid_argument("Requested frequency out of range\n"); + } + + int div = 1; + double lo_freq = freq * 2; + while((lo_freq < 1e9) && (div < 8)) { + div = div * 2; + lo_freq = lo_freq * 2; + } + + printf("For RF freq of %f, we set DIV=%d and LO Freq=%f\n", freq, div, lo_freq); + + set_divider("main", div); + set_divider("aux", div*2); + + int R, N, control; + double actual_freq; + _compute_regs(lo_freq, R, N, control, actual_freq); + + printf("R %d N %d control %d actual freq %f\n", R, N, control, actual_freq); + if(R==0) { + ret.ok = false; + ret.baseband_freq = 0.0; + return ret; + } + _write_all(R, N, control); + + ret.ok = _lock_detect(); + ret.baseband_freq = actual_freq/div/2; + return ret; +} + + +/****************************************************************************/ + + +db_wbx_lo_tx::db_wbx_lo_tx(usrp_basic_sptr usrp, int which) + : _lo_common(), + wbx_base_tx(usrp, which) +{ +} + +db_wbx_lo_tx::~db_wbx_lo_tx() +{ +} + +float +db_wbx_lo_tx::gain_min() +{ + return -56.0; +} + +float +db_wbx_lo_tx::gain_max() +{ + return 0.0; +} + +float +db_wbx_lo_tx::gain_db_per_step() +{ + return 0.1; +} + +bool +db_wbx_lo_tx::set_gain(float gain) +{ + /* + * Set the gain. + * + * @param gain: gain in decibels + * @returns True/False + */ + + float txvga_gain; + float maxgain = gain_max(); + float mingain = gain_min(); + if(gain > maxgain) { + txvga_gain = maxgain; + } + else if(gain < mingain) { + txvga_gain = mingain; + } + else { + txvga_gain = gain; + } + + float V_maxgain = 1.4; + float V_mingain = 0.1; + float V_fullscale = 3.3; + float dac_value = ((txvga_gain-mingain)*(V_maxgain-V_mingain)/ + (maxgain-mingain) + V_mingain)*4096/V_fullscale; + + assert(dac_value>=0 && dac_value<4096); + printf("DAC value %f\n", dac_value); + + return d_usrp->write_aux_dac(d_which, 1, (int)(dac_value)); +} + +double +db_wbx_lo_tx::_refclk_freq() +{ + return wbx_base::_refclk_freq(); +} + +bool +db_wbx_lo_tx::_rx_write_io(int value, int mask) +{ + return wbx_base::_rx_write_io(value, mask); +} + +bool +db_wbx_lo_tx::_lock_detect() +{ + return wbx_base::_lock_detect(); +} + +usrp_basic* +db_wbx_lo_tx::usrp() +{ + return d_usrp; +} + + +/****************************************************************************/ + + +db_wbx_lo_rx::db_wbx_lo_rx(usrp_basic_sptr usrp, int which) + : _lo_common(), + wbx_base_rx(usrp, which) +{ +} + +db_wbx_lo_rx::~db_wbx_lo_rx() +{ +} + +float +db_wbx_lo_rx::gain_min() +{ + return d_usrp->pga_min(); +} + +float +db_wbx_lo_rx::gain_max() +{ + return d_usrp->pga_max() + 45; +} + +float +db_wbx_lo_rx::gain_db_per_step() +{ + return 0.05; +} + +double +db_wbx_lo_rx::_refclk_freq() +{ + return wbx_base::_refclk_freq(); +} + +bool +db_wbx_lo_rx::_rx_write_io(int value, int mask) +{ + return wbx_base::_rx_write_io(value, mask); +} + +bool +db_wbx_lo_rx::_lock_detect() +{ + return wbx_base::_lock_detect(); +} + +usrp_basic* +db_wbx_lo_rx::usrp() +{ + return d_usrp; +} diff --git a/usrp/host/lib/legacy/db_wbx.h b/usrp/host/lib/legacy/db_wbx.h new file mode 100644 index 000000000..3202d368c --- /dev/null +++ b/usrp/host/lib/legacy/db_wbx.h @@ -0,0 +1,221 @@ +/* -*- 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 asversion 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 DB_WBX_H +#define DB_WBX_H + +#include <db_base.h> +#include <boost/shared_ptr.hpp> + + +/* + A few comments about the WBX boards: + They are half-duplex. I.e., transmit and receive are mutually exclusive. + There is a single LO for both the Tx and Rx sides. + The the shared control signals are hung off of the Rx side. + The shared io controls are duplexed onto the Rx side pins. + The wbx_high d'board always needs to be in 'auto_tr_mode' +*/ + + +class wbx_base : public db_base +{ +protected: + void shutdown(); + + /* + * Abstract base class for all wbx boards. + * + * Derive board specific subclasses from db_wbx_base_{tx,rx} + */ + +public: + wbx_base(usrp_basic_sptr usrp, int which); + ~wbx_base(); + + struct freq_result_t set_freq(double freq); + float gain_min(); + float gain_max(); + float gain_db_per_step(); + bool set_gain(float gain); + bool is_quadrature(); + + +protected: + virtual bool _lock_detect(); + + // FIXME: After testing, replace these with usrp_basic::common_write_io/oe + bool _tx_write_oe(int value, int mask); + bool _rx_write_oe(int value, int mask); + bool _tx_write_io(int value, int mask); + bool _rx_write_io(int value, int mask); + virtual bool _rx_read_io(); + bool _tx_read_io(); + bool _compute_regs(double freq); + virtual double _refclk_freq(); + int _refclk_divisor(); + + bool _set_pga(float pga_gain); + + bool d_first; + int d_spi_format; + int d_spi_enable; + double d_lo_offset; +}; + + +/****************************************************************************/ + + +class wbx_base_tx : public wbx_base +{ +public: + wbx_base_tx(usrp_basic_sptr usrp, int which); + ~wbx_base_tx(); + + bool set_auto_tr(bool on); + bool set_enable(bool on); +}; + + +/****************************************************************************/ + + +class wbx_base_rx : public wbx_base +{ +public: + wbx_base_rx(usrp_basic_sptr usrp, int which); + ~wbx_base_rx(); + + bool set_auto_tr(bool on); + bool select_rx_antenna(int which_antenna); + bool select_rx_antenna(const std::string &which_antenna); + bool set_gain(float gain); + bool i_and_q_swapped(); +}; + + +/****************************************************************************/ + + +class _ADF410X_common +{ +public: + _ADF410X_common(); + virtual ~_ADF410X_common(); + + bool _compute_regs(double freq, int &retR, int &retcontrol, + int &retN, double &retfreq); + void _write_all(int R, int N, int control); + void _write_R(int R); + void _write_N(int N); + void _write_func(int func); + void _write_init(int init); + int _prescaler(); + virtual void _write_it(int v); + virtual double _refclk_freq(); + virtual bool _rx_write_io(int value, int mask); + virtual bool _lock_detect(); + +protected: + virtual usrp_basic* usrp(); + + int d_R_RSV, d_LDP, d_TEST, d_ABP; + int d_N_RSV, d_CP_GAIN; + int d_P, d_PD2, d_CP2, d_CP1, d_TC, d_FL; + int d_CP3S, d_PDP, d_MUXOUT, d_PD1, d_CR; + int d_R_DIV, d_A_DIV, d_B_DIV; + int d_freq_mult; + + int d_spi_format; + int d_spi_enable; +}; + + +/****************************************************************************/ + + +class _lo_common : public _ADF410X_common +{ +public: + _lo_common(); + ~_lo_common(); + + double freq_min(); + double freq_max(); + + void set_divider(int main_or_aux, int divisor); + void set_divider(const std::string &main_or_aux, int divisor); + + struct freq_result_t set_freq(double freq); + +protected: + int d_R_DIV, d_P, d_CP2, d_CP1; + int d_DIVSEL, d_DIV2, d_CPGAIN; + int d_div, d_aux_div, d_main_div; +}; + + +/****************************************************************************/ + + +class db_wbx_lo_tx : public _lo_common, public wbx_base_tx +{ +public: + db_wbx_lo_tx(usrp_basic_sptr usrp, int which); + ~db_wbx_lo_tx(); + + float gain_min(); + float gain_max(); + float gain_db_per_step(); + bool set_gain(float gain); + + double _refclk_freq(); + bool _rx_write_io(int value, int mask); + bool _lock_detect(); + +protected: + usrp_basic* usrp(); +}; + + +/****************************************************************************/ + + +class db_wbx_lo_rx : public _lo_common, public wbx_base_rx +{ +public: + db_wbx_lo_rx(usrp_basic_sptr usrp, int which); + ~db_wbx_lo_rx(); + + float gain_min(); + float gain_max(); + float gain_db_per_step(); + + double _refclk_freq(); + bool _rx_write_io(int value, int mask); + bool _lock_detect(); + +protected: + usrp_basic* usrp(); +}; + +#endif diff --git a/usrp/host/lib/legacy/db_xcvr2450.cc b/usrp/host/lib/legacy/db_xcvr2450.cc new file mode 100644 index 000000000..e0c3f91c2 --- /dev/null +++ b/usrp/host/lib/legacy/db_xcvr2450.cc @@ -0,0 +1,762 @@ +// +// 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 asversion 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 <db_xcvr2450.h> +#include <db_base_impl.h> +#include <cmath> + + +/* ------------------------------------------------------------------------ + * A few comments about the XCVR2450: + * + * It is half-duplex. I.e., transmit and receive are mutually exclusive. + * There is a single LO for both the Tx and Rx sides. + * For our purposes the board is always either receiving or transmitting. + * + * Each board is uniquely identified by the *USRP hardware* instance and side + * This dictionary holds a weak reference to existing board controller so it + * can be created or retrieved as needed. + */ + + +/*****************************************************************************/ + + +xcvr2450::xcvr2450(usrp_basic_sptr _usrp, int which) + : d_weak_usrp(_usrp), d_which(which) +{ + // Handler for Tv Rx daughterboards. + // + // @param usrp: instance of usrp.source_c + // @param which: which side: 0, 1 corresponding to RX_A or RX_B respectively + + // Use MSB with no header + d_spi_format = SPI_FMT_MSB | SPI_FMT_HDR_0; + + if(which == 0) { + d_spi_enable = SPI_ENABLE_RX_A; + } + else { + d_spi_enable = SPI_ENABLE_RX_B; + } + + // Sane defaults + d_mimo = 1; // 0 = OFF, 1 = ON + d_int_div = 192; // 128 = min, 255 = max + d_frac_div = 0; // 0 = min, 65535 = max + d_highband = 0; // 0 = freq <= 5.4e9, 1 = freq > 5.4e9 + d_five_gig = 0; // 0 = freq <= 3.e9, 1 = freq > 3e9 + d_cp_current = 0; // 0 = 2mA, 1 = 4mA + d_ref_div = 4; // 1 to 7 + d_rssi_hbw = 0; // 0 = 2 MHz, 1 = 6 MHz + d_txlpf_bw = 1; // 1 = 12 MHz, 2 = 18 MHz, 3 = 24 MHz + d_rxlpf_bw = 1; // 0 = 7.5 MHz, 1 = 9.5 MHz, 2 = 14 MHz, 3 = 18 MHz + d_rxlpf_fine = 2; // 0 = 90%, 1 = 95%, 2 = 100%, 3 = 105%, 4 = 110% + d_rxvga_ser = 1; // 0 = RXVGA controlled by B7:1, 1=controlled serially + d_rssi_range = 1; // 0 = low range (datasheet typo), 1=high range (0.5V - 2.0V) + d_rssi_mode = 1; // 0 = enable follows RXHP, 1 = enabled + d_rssi_mux = 0; // 0 = RSSI, 1 = TEMP + d_rx_hp_pin = 0; // 0 = Fc set by rx_hpf, 1 = 600 KHz + d_rx_hpf = 0; // 0 = 100Hz, 1 = 30KHz + d_rx_ant = 0; // 0 = Ant. #1, 1 = Ant. #2 + d_tx_ant = 0; // 0 = Ant. #1, 1 = Ant. #2 + d_txvga_ser = 1; // 0 = TXVGA controlled by B6:1, 1=controlled serially + d_tx_driver_lin = 2; // 0=50% (worst linearity), 1=63%, 2=78%, 3=100% (best lin) + d_tx_vga_lin = 2; // 0=50% (worst linearity), 1=63%, 2=78%, 3=100% (best lin) + d_tx_upconv_lin = 2; // 0=50% (worst linearity), 1=63%, 2=78%, 3=100% (best lin) + d_tx_bb_gain = 3; // 0=maxgain-5dB, 1=max-3dB, 2=max-1.5dB, 3=max + d_pabias_delay = 15; // 0 = 0, 15 = 7uS + d_pabias = 0; // 0 = 0 uA, 63 = 315uA + d_rx_rf_gain = 0; // 0 = 0dB, 1 = 0dB, 2 = 15dB, 3 = 30dB + d_rx_bb_gain = 16; // 0 = min, 31 = max (0 - 62 dB) + + d_txgain = 63; // 0 = min, 63 = max + + // Initialize GPIO and ATR + tx_write_io(TX_SAFE_IO, TX_OE_MASK); + tx_write_oe(TX_OE_MASK, ~0); + tx_set_atr_txval(TX_SAFE_IO); + tx_set_atr_rxval(TX_SAFE_IO); + tx_set_atr_mask(TX_OE_MASK); + rx_write_io(RX_SAFE_IO, RX_OE_MASK); + rx_write_oe(RX_OE_MASK, ~0); + rx_set_atr_rxval(RX_SAFE_IO); + rx_set_atr_txval(RX_SAFE_IO); + rx_set_atr_mask(RX_OE_MASK); + + // Initialize chipset + // TODO: perform reset sequence to ensure power up defaults + set_reg_standby(); + set_reg_bandselpll(); + set_reg_cal(); + set_reg_lpf(); + set_reg_rxrssi_ctrl(); + set_reg_txlin_gain(); + set_reg_pabias(); + set_reg_rxgain(); + set_reg_txgain(); + //FIXME: set_freq(2.45e9); +} + +xcvr2450::~xcvr2450() +{ + //printf("xcvr2450::destructor\n"); + tx_set_atr_txval(TX_SAFE_IO); + tx_set_atr_rxval(TX_SAFE_IO); + rx_set_atr_rxval(RX_SAFE_IO); + rx_set_atr_txval(RX_SAFE_IO); +} + +bool +xcvr2450::operator==(xcvr2450_key x) +{ + if((x.serial_no == usrp()->serial_number()) && (x.which == d_which)) { + return true; + } + else { + return false; + } +} + +void +xcvr2450::set_reg_standby() +{ + d_reg_standby = ((d_mimo<<17) | + (1<<16) | + (1<<6) | + (1<<5) | + (1<<4) | 2); + send_reg(d_reg_standby); +} + +void +xcvr2450::set_reg_int_divider() +{ + d_reg_int_divider = (((d_frac_div & 0x03)<<16) | + (d_int_div<<4) | 3); + send_reg(d_reg_int_divider); +} + +void +xcvr2450::set_reg_frac_divider() +{ + d_reg_frac_divider = ((d_frac_div & 0xfffc)<<2) | 4; + send_reg(d_reg_frac_divider); +} + +void +xcvr2450::set_reg_bandselpll() +{ + d_reg_bandselpll = ((d_mimo<<17) | + (1<<16) | + (1<<15) | + (1<<11) | + (d_highband<<10) | + (d_cp_current<<9) | + (d_ref_div<<5) | + (d_five_gig<<4) | 5); + send_reg(d_reg_bandselpll); +} + +void +xcvr2450::set_reg_cal() +{ + // FIXME do calibration + d_reg_cal = (1<<14)|6; + send_reg(d_reg_cal); +} + +void +xcvr2450::set_reg_lpf() +{ + d_reg_lpf = ( + (d_rssi_hbw<<15) | + (d_txlpf_bw<<10) | + (d_rxlpf_bw<<9) | + (d_rxlpf_fine<<4) | 7); + send_reg(d_reg_lpf); +} + +void +xcvr2450::set_reg_rxrssi_ctrl() +{ + d_reg_rxrssi_ctrl = ((d_rxvga_ser<<16) | + (d_rssi_range<<15) | + (d_rssi_mode<<14) | + (d_rssi_mux<<12) | + (1<<9) | + (d_rx_hpf<<6) | + (1<<4) | 8); + send_reg(d_reg_rxrssi_ctrl); +} + +void +xcvr2450::set_reg_txlin_gain() +{ + d_reg_txlin_gain = ((d_txvga_ser<<14) | + (d_tx_driver_lin<<12) | + (d_tx_vga_lin<<10) | + (d_tx_upconv_lin<<6) | + (d_tx_bb_gain<<4) | 9); + send_reg(d_reg_txlin_gain); +} + +void +xcvr2450::set_reg_pabias() +{ + d_reg_pabias = ( + (d_pabias_delay<<10) | + (d_pabias<<4) | 10); + send_reg(d_reg_pabias); +} + +void +xcvr2450::set_reg_rxgain() +{ + d_reg_rxgain = ( + (d_rx_rf_gain<<9) | + (d_rx_bb_gain<<4) | 11); + send_reg(d_reg_rxgain); +} + +void +xcvr2450::set_reg_txgain() +{ + d_reg_txgain = (d_txgain<<4) | 12; + send_reg(d_reg_txgain); +} + +void +xcvr2450::send_reg(int v) +{ + // Send 24 bits, it keeps last 18 clocked in + char c[3]; + c[0] = (char)((v >> 16) & 0xff); + c[1] = (char)((v >> 8) & 0xff); + c[2] = (char)((v & 0xff)); + std::string s(c, 3); + + usrp()->_write_spi(0, d_spi_enable, d_spi_format, s); + //printf("xcvr2450: Setting reg %d to %06X\n", (v&15), v); +} + +// -------------------------------------------------------------------- +// These methods control the GPIO bus. Since the board has to access +// both the io_rx_* and io_tx_* pins, we define our own methods to do so. +// This bypasses any code in db_base. +// +// The board operates in ATR mode, always. Thus, when the board is first +// initialized, it is in receive mode, until bits show up in the TX FIFO. +// + +// FIXME these should just call the similarly named common_* method on usrp_basic + +bool +xcvr2450::tx_write_oe(int value, int mask) +{ + int reg; + if(d_which) + reg = FR_OE_0; + else + reg = FR_OE_2; + return usrp()->_write_fpga_reg(reg, (mask << 16) | value); +} + +bool +xcvr2450::tx_write_io(int value, int mask) +{ + int reg; + if(d_which) + reg = FR_IO_0; + else + reg = FR_IO_2; + return usrp()->_write_fpga_reg(reg, (mask << 16) | value); +} + +int +xcvr2450::tx_read_io() +{ + int val; + if(d_which) + val = FR_RB_IO_RX_A_IO_TX_A; + else + val = FR_RB_IO_RX_B_IO_TX_B; + int t = usrp()->_read_fpga_reg(val); + return t & 0xffff; +} + +bool +xcvr2450::rx_write_oe(int value, int mask) +{ + int reg; + if(d_which) + reg = FR_OE_1; + else + reg = FR_OE_3; + return usrp()->_write_fpga_reg(reg, (mask << 16) | value); +} + +bool +xcvr2450::rx_write_io(int value, int mask) +{ + int reg; + if(d_which) + reg = FR_IO_1; + else + reg = FR_IO_3; + return usrp()->_write_fpga_reg(reg, (mask << 16) | value); +} + +int +xcvr2450::rx_read_io() +{ + int val; + if(d_which) + val = FR_RB_IO_RX_A_IO_TX_A; + else + val = FR_RB_IO_RX_B_IO_TX_B; + int t = usrp()->_read_fpga_reg(val); + return (t >> 16) & 0xffff; +} + +bool +xcvr2450::tx_set_atr_mask(int v) +{ + int reg; + if(d_which) + reg = FR_ATR_MASK_0; + else + reg = FR_ATR_MASK_2; + return usrp()->_write_fpga_reg(reg, v); +} + +bool +xcvr2450::tx_set_atr_txval(int v) +{ + int reg; + if(d_which) + reg = FR_ATR_TXVAL_0; + else + reg = FR_ATR_TXVAL_2; + return usrp()->_write_fpga_reg(reg, v); +} + +bool +xcvr2450::tx_set_atr_rxval(int v) +{ + int reg; + if(d_which) + reg = FR_ATR_RXVAL_0; + else + reg = FR_ATR_RXVAL_2; + return usrp()->_write_fpga_reg(reg, v); +} + +bool +xcvr2450::rx_set_atr_mask(int v) +{ + int reg; + if(d_which) + reg = FR_ATR_MASK_1; + else + reg = FR_ATR_MASK_3; + return usrp()->_write_fpga_reg(reg, v); +} + +bool +xcvr2450::rx_set_atr_txval(int v) +{ + int reg; + if(d_which) + reg = FR_ATR_TXVAL_1; + else + reg = FR_ATR_TXVAL_3; + return usrp()->_write_fpga_reg(reg, v); +} + +bool +xcvr2450::rx_set_atr_rxval(int v) +{ + int reg; + if(d_which) + reg = FR_ATR_RXVAL_1; + else + reg = FR_ATR_RXVAL_3; + return usrp()->_write_fpga_reg(reg, v); +} + +// ---------------------------------------------------------------- + +void +xcvr2450::set_gpio() +{ + // We calculate four values: + // + // io_rx_while_rx: what to drive onto io_rx_* when receiving + // io_rx_while_tx: what to drive onto io_rx_* when transmitting + // io_tx_while_rx: what to drive onto io_tx_* when receiving + // io_tx_while_tx: what to drive onto io_tx_* when transmitting + // + // B1-B7 is ignored as gain is set serially for now. + + int rx_hp, tx_antsel, rx_antsel, tx_pa_sel; + if(d_rx_hp_pin) + rx_hp = 0; + else + rx_hp = RX_HP; + + if(d_tx_ant) + tx_antsel = ANTSEL_TX1_RX2; + else + tx_antsel = ANTSEL_TX2_RX1; + + if(d_rx_ant) + rx_antsel = ANTSEL_TX1_RX2; + else + rx_antsel = ANTSEL_TX2_RX1; + + if(d_five_gig) + tx_pa_sel = HB_PA_OFF; + else + tx_pa_sel = LB_PA_OFF; + + int io_rx_while_rx = EN|rx_hp|RX_EN; + int io_rx_while_tx = EN|rx_hp; + int io_tx_while_rx = HB_PA_OFF|LB_PA_OFF|rx_antsel|AD9515DIV; + int io_tx_while_tx = tx_pa_sel|tx_antsel|TX_EN|AD9515DIV; + rx_set_atr_rxval(io_rx_while_rx); + rx_set_atr_txval(io_rx_while_tx); + tx_set_atr_rxval(io_tx_while_rx); + tx_set_atr_txval(io_tx_while_tx); + + //printf("GPIO: RXRX=%04X RXTX=%04X TXRX=%04X TXTX=%04X", + // io_rx_while_rx, io_rx_while_tx, io_tx_while_rx, io_tx_while_tx); +} + + +struct freq_result_t +xcvr2450::set_freq(double target_freq) +{ + struct freq_result_t args = {false, 0}; + + double scaler; + + if(target_freq > 3e9) { + d_five_gig = 1; + d_ref_div = 1; + d_ad9515_div = 3; + scaler = 4.0/5.0; + } + else { + d_five_gig = 0; + d_ref_div = 1; + d_ad9515_div = 3; + scaler = 4.0/3.0; + } + + if(target_freq > 5.27e9) { + d_highband = 1; + } + else { + d_highband = 0; + } + + double vco_freq = target_freq*scaler; + double sys_clk = usrp()->fpga_master_clock_freq(); // Usually 64e6 + double ref_clk = sys_clk / d_ad9515_div; + + double phdet_freq = ref_clk/d_ref_div; + double div = vco_freq/phdet_freq; + d_int_div = int(floor(div)); + d_frac_div = int((div-d_int_div)*65536.0); + double actual_freq = phdet_freq*(d_int_div+(d_frac_div/65536.0))/scaler; + + //printf("RF=%f VCO=%f R=%d PHD=%f DIV=%3.5f I=%3d F=%5d ACT=%f", + // target_freq, vco_freq, d_ref_div, phdet_freq, + // div, d_int_div, d_frac_div, actual_freq); + + set_gpio(); + set_reg_int_divider(); + set_reg_frac_divider(); + set_reg_bandselpll(); + + args.ok = lock_detect(); + args.baseband_freq = actual_freq; + + if(args.ok) { + if((target_freq > 5.275e9) && (target_freq <= 5.35e9)) { + d_highband = 0; + set_reg_bandselpll(); + args.ok = lock_detect(); + printf("swap to 0 at %f, ok %d\n", target_freq, args.ok); + } + if((target_freq >= 5.25e9) && (target_freq <= 5.275e9)) { + d_highband = 1; + set_reg_bandselpll(); + args.ok = lock_detect(); + printf("swap to 1 at %f, ok %d\n", target_freq, args.ok); + } + if(!args.ok){ + printf("Fail %f\n", target_freq); + } + } + return args; +} + +bool +xcvr2450::lock_detect() +{ + /* + @returns: the value of the VCO/PLL lock detect bit. + @rtype: 0 or 1 + */ + if(rx_read_io() & LOCKDET) { + return true; + } + else { // Give it a second chance + if(rx_read_io() & LOCKDET) + return true; + else + return false; + } +} + +bool +xcvr2450::set_rx_gain(float gain) +{ + if(gain < 0.0) + gain = 0.0; + if(gain > 92.0) + gain = 92.0; + + // Split the gain between RF and baseband + // This is experimental, not prescribed + if(gain < 31.0) { + d_rx_rf_gain = 0; // 0 dB RF gain + rx_bb_gain = int(gain/2.0); + } + + if(gain >= 30.0 and gain < 60.5) { + d_rx_rf_gain = 2; // 15 dB RF gain + d_rx_bb_gain = int((gain-15.0)/2.0); + } + + if(gain >= 60.5) { + d_rx_rf_gain = 3; // 30.5 dB RF gain + d_rx_bb_gain = int((gain-30.5)/2.0); + } + + set_reg_rxgain(); + + return true; +} + +bool +xcvr2450::set_tx_gain(float gain) +{ + if(gain < 0.0) { + gain = 0.0; + } + if(gain > 30.0) { + gain = 30.0; + } + + d_txgain = int((gain/30.0)*63); + set_reg_txgain(); + + return true; +} + + +/*****************************************************************************/ + + +//_xcvr2450_inst = weakref.WeakValueDictionary() +std::vector<xcvr2450_sptr> _xcvr2450_inst; + +xcvr2450_sptr +_get_or_make_xcvr2450(usrp_basic_sptr usrp, int which) +{ + xcvr2450_sptr inst; + xcvr2450_key key = {usrp->serial_number(), which}; + std::vector<xcvr2450_sptr>::iterator itr; // = + //std::find(_xcvr2450_inst.begin(), _xcvr2450_inst.end(), key); + + for(itr = _xcvr2450_inst.begin(); itr != _xcvr2450_inst.end(); itr++) { + if(*(*itr) == key) { + printf("Using existing xcvr2450 instance\n"); + inst = *itr; + break; + } + } + + if(itr != _xcvr2450_inst.end()) { + printf("Creating new xcvr2450 instance\n"); + inst = xcvr2450_sptr(new xcvr2450(usrp, which)); + _xcvr2450_inst.push_back(inst); + } + + return inst; +} + + +/*****************************************************************************/ + + +db_xcvr2450_base::db_xcvr2450_base(usrp_basic_sptr usrp, int which) + : db_base(usrp, which) +{ + /* + * Abstract base class for all xcvr2450 boards. + * + * Derive board specific subclasses from db_xcvr2450_base_{tx,rx} + * + * @param usrp: instance of usrp.source_c + * @param which: which side: 0 or 1 corresponding to side A or B respectively + * @type which: int + */ + + d_xcvr = _get_or_make_xcvr2450(usrp, which); +} + +db_xcvr2450_base::~db_xcvr2450_base() +{ +} + +struct freq_result_t +db_xcvr2450_base::set_freq(double target_freq) +{ + /* + * @returns (ok, actual_baseband_freq) where: + * ok is True or False and indicates success or failure, + * actual_baseband_freq is the RF frequency that corresponds to DC in the IF. + */ + return d_xcvr->set_freq(target_freq); +} + +bool +db_xcvr2450_base::is_quadrature() +{ + /* + * Return True if this board requires both I & Q analog channels. + * + * This bit of info is useful when setting up the USRP Rx mux register. + */ + return true; +} + +double +db_xcvr2450_base::freq_min() +{ + return 2.4e9; +} + +double +db_xcvr2450_base::freq_max() +{ + return 6.0e9; +} + + +/******************************************************************************/ + + +db_xcvr2450_tx::db_xcvr2450_tx(usrp_basic_sptr usrp, int which) + : db_xcvr2450_base(usrp, which) +{ + printf("db_xcvr2450_tx::db_xcvr2450_tx\n"); +} + +db_xcvr2450_tx::~db_xcvr2450_tx() +{ +} + +float +db_xcvr2450_tx::gain_min() +{ + return 0; +} + +float +db_xcvr2450_tx::gain_max() +{ + return 30; +} + +float +db_xcvr2450_tx::gain_db_per_step() +{ + return (30.0/63.0); +} + +bool +db_xcvr2450_tx::set_gain(float gain) +{ + return d_xcvr->set_tx_gain(gain); +} + +bool +db_xcvr2450_tx::i_and_q_swapped() +{ + return true; +} + + +/******************************************************************************/ + + +db_xcvr2450_rx::db_xcvr2450_rx(usrp_basic_sptr usrp, int which) + : db_xcvr2450_base(usrp, which) +{ + /* + * @param usrp: instance of usrp.source_c + * @param which: 0 or 1 corresponding to side RX_A or RX_B respectively. + */ + + printf("db_xcvr2450_rx:d_xcvr_2450_rx\n"); +} + +db_xcvr2450_rx::~db_xcvr2450_rx() +{ +} + +float +db_xcvr2450_rx::gain_min() +{ + return 0.0; +} + +float +db_xcvr2450_rx::gain_max() +{ + return 92.0; +} + +float +db_xcvr2450_rx::gain_db_per_step() +{ + return 1; +} + +bool +db_xcvr2450_rx::set_gain(float gain) +{ + return d_xcvr->set_rx_gain(gain); +} diff --git a/usrp/host/lib/legacy/db_xcvr2450.h b/usrp/host/lib/legacy/db_xcvr2450.h new file mode 100644 index 000000000..a67c4302f --- /dev/null +++ b/usrp/host/lib/legacy/db_xcvr2450.h @@ -0,0 +1,221 @@ +/* -*- 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 asversion 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 DB_XCVR2450_H +#define DB_XCVR2450_H + +#include <db_base.h> +#include <boost/shared_ptr.hpp> + +// TX IO Pins +#define HB_PA_OFF (1 << 15) // 5GHz PA, 1 = off, 0 = on +#define LB_PA_OFF (1 << 14) // 2.4GHz PA, 1 = off, 0 = on +#define ANTSEL_TX1_RX2 (1 << 13) // 1 = Ant 1 to TX, Ant 2 to RX +#define ANTSEL_TX2_RX1 (1 << 12) // 1 = Ant 2 to TX, Ant 1 to RX +#define TX_EN (1 << 11) // 1 = TX on, 0 = TX off +#define AD9515DIV (1 << 4) // 1 = Div by 3, 0 = Div by 2 + +#define TX_OE_MASK HB_PA_OFF|LB_PA_OFF|ANTSEL_TX1_RX2|ANTSEL_TX2_RX1|TX_EN|AD9515DIV +#define TX_SAFE_IO HB_PA_OFF|LB_PA_OFF|ANTSEL_TX1_RX2|AD9515DIV + +// RX IO Pins +#define LOCKDET (1 << 15) // This is an INPUT!!! +#define EN (1 << 14) +#define RX_EN (1 << 13) // 1 = RX on, 0 = RX off +#define RX_HP (1 << 12) +#define B1 (1 << 11) +#define B2 (1 << 10) +#define B3 (1 << 9) +#define B4 (1 << 8) +#define B5 (1 << 7) +#define B6 (1 << 6) +#define B7 (1 << 5) +#define RX_OE_MASK EN|RX_EN|RX_HP|B1|B2|B3|B4|B5|B6|B7 +#define RX_SAFE_IO EN + +struct xcvr2450_key { + std::string serial_no; + int which; +}; + +class xcvr2450; +typedef boost::shared_ptr<xcvr2450> xcvr2450_sptr; + +class xcvr2450 +{ +private: + boost::weak_ptr<usrp_basic> d_weak_usrp; + int d_which; + + int d_spi_format, d_spi_enable; + + int d_mimo, d_int_div, d_frac_div, d_highband, d_five_gig; + int d_cp_current, d_ref_div, d_rssi_hbw; + int d_txlpf_bw, d_rxlpf_bw, d_rxlpf_fine, d_rxvga_ser; + int d_rssi_range, d_rssi_mode, d_rssi_mux; + int d_rx_hp_pin, d_rx_hpf, d_rx_ant; + int d_tx_ant, d_txvga_ser, d_tx_driver_lin; + int d_tx_vga_lin, d_tx_upconv_lin, d_tx_bb_gain; + int d_pabias_delay, d_pabias, rx_rf_gain, rx_bb_gain, d_txgain; + int d_rx_rf_gain, d_rx_bb_gain; + + int d_reg_standby, d_reg_int_divider, d_reg_frac_divider, d_reg_bandselpll; + int d_reg_cal, dsend_reg, d_reg_lpf, d_reg_rxrssi_ctrl, d_reg_txlin_gain; + int d_reg_pabias, d_reg_rxgain, d_reg_txgain; + + int d_ad9515_div; + + void _set_rfagc(float gain); + void _set_ifagc(float gain); + void _set_pga(float pga_gain); + + usrp_basic_sptr usrp(){ + return usrp_basic_sptr(d_weak_usrp); // throws bad_weak_ptr if d_usrp.use_count() == 0 + } + +public: + xcvr2450(usrp_basic_sptr usrp, int which); + ~xcvr2450(); + + bool operator==(xcvr2450_key x); + + void set_reg_standby(); + + // Integer-Divider Ratio (3) + void set_reg_int_divider(); + + // Fractional-Divider Ratio (4) + void set_reg_frac_divider(); + + // Band Select and PLL (5) + void set_reg_bandselpll(); + + // Calibration (6) + void set_reg_cal(); + + // Lowpass Filter (7) + void set_reg_lpf(); + + // Rx Control/RSSI (8) + void set_reg_rxrssi_ctrl(); + + // Tx Linearity/Baseband Gain (9) + void set_reg_txlin_gain(); + + // PA Bias DAC (10) + void set_reg_pabias(); + + // Rx Gain (11) + void set_reg_rxgain(); + + // Tx Gain (12) + void set_reg_txgain(); + + // Send register write to SPI + void send_reg(int v); + + // -------------------------------------------------------------------- + // These methods control the GPIO bus. Since the board has to access + // both the io_rx_* and io_tx_* pins, we define our own methods to do so. + // This bypasses any code in db_base. + // + // The board operates in ATR mode, always. Thus, when the board is first + // initialized, it is in receive mode, until bits show up in the TX FIFO. + // + + // FIXME these should just call the similarly named common_* method on usrp_basic + + bool tx_write_oe(int value, int mask); + bool tx_write_io(int value, int mask); + int tx_read_io(); + bool rx_write_oe(int value, int mask); + bool rx_write_io(int value, int mask); + int rx_read_io(); + bool tx_set_atr_mask(int v); + bool tx_set_atr_txval(int v); + bool tx_set_atr_rxval(int v); + bool rx_set_atr_mask(int v); + bool rx_set_atr_txval(int v); + bool rx_set_atr_rxval(int v); + + void set_gpio(); + bool lock_detect(); + bool set_rx_gain(float gain); + bool set_tx_gain(float gain); + + struct freq_result_t set_freq(double target_freq); +}; + + +/******************************************************************************/ + + +class db_xcvr2450_base: public db_base +{ + /* + * Abstract base class for all xcvr2450 boards. + * + * Derive board specific subclasses from db_xcvr2450_base_{tx,rx} + */ +public: + db_xcvr2450_base(usrp_basic_sptr usrp, int which); + ~db_xcvr2450_base(); + struct freq_result_t set_freq(double target_freq); + bool is_quadrature(); + double freq_min(); + double freq_max(); + +protected: + xcvr2450_sptr d_xcvr; +}; + + +/******************************************************************************/ + + +class db_xcvr2450_tx : public db_xcvr2450_base +{ +public: + db_xcvr2450_tx(usrp_basic_sptr usrp, int which); + ~db_xcvr2450_tx(); + + float gain_min(); + float gain_max(); + float gain_db_per_step(); + bool set_gain(float gain); + bool i_and_q_swapped(); +}; + +class db_xcvr2450_rx : public db_xcvr2450_base +{ +public: + db_xcvr2450_rx(usrp_basic_sptr usrp, int which); + ~db_xcvr2450_rx(); + + float gain_min(); + float gain_max(); + float gain_db_per_step(); + bool set_gain(float gain); +}; + + + +#endif diff --git a/usrp/host/lib/legacy/usrp_basic.cc b/usrp/host/lib/legacy/usrp_basic.cc index f67300306..e63a097ac 100644 --- a/usrp/host/lib/legacy/usrp_basic.cc +++ b/usrp/host/lib/legacy/usrp_basic.cc @@ -1,6 +1,6 @@ /* -*- c++ -*- */ /* - * Copyright 2003,2004 Free Software Foundation, Inc. + * Copyright 2003,2004,2008 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -28,7 +28,9 @@ #include "usrp_prims.h" #include "usrp_interfaces.h" #include "fpga_regs_common.h" +#include "fpga_regs_standard.h" #include "fusb.h" +#include "db_boards.h" #include <usb.h> #include <stdexcept> #include <assert.h> @@ -110,7 +112,7 @@ usrp_basic::usrp_basic (int which_board, : d_udh (0), d_usb_data_rate (16000000), // SWAG, see below d_bytes_per_poll ((int) (POLLING_INTERVAL * d_usb_data_rate)), - d_verbose (false) + d_verbose (false), d_db(2) { /* * SWAG: Scientific Wild Ass Guess. @@ -154,12 +156,65 @@ usrp_basic::usrp_basic (int which_board, _write_fpga_reg (FR_DEBUG_EN, 0); // disable debug outputs } +void +usrp_basic::shutdown_daughterboards() +{ + // nuke d'boards before we close down USB in ~usrp_basic + // shutdown() will do any board shutdown while the USRP can still + // be talked to + for(size_t i = 0; i < d_db.size(); i++) + for(size_t j = 0; j < d_db[i].size(); j++) + d_db[i][j]->shutdown(); +} + usrp_basic::~usrp_basic () { + // shutdown_daughterboards(); // call from ~usrp_basic_{tx,rx} + + d_db.resize(0); // forget db shared ptrs + if (d_udh) usb_close (d_udh); } +void +usrp_basic::init_db(usrp_basic_sptr u) +{ + if (u.get() != this) + throw std::invalid_argument("u is not this"); + + d_db[0] = instantiate_dbs(d_dbid[0], u, 0); + d_db[1] = instantiate_dbs(d_dbid[1], u, 1); +} + +std::vector<db_base_sptr> +usrp_basic::db(int which_side) +{ + which_side &= 0x1; // clamp it to avoid any reporting any errors + return d_db[which_side]; +} + +bool +usrp_basic::is_valid(const usrp_subdev_spec &ss) +{ + if (ss.side < 0 || ss.side > 1) + return false; + + if (ss.subdev < 0 || ss.subdev >= d_db[ss.side].size()) + return false; + + return true; +} + +db_base_sptr +usrp_basic::selected_subdev(const usrp_subdev_spec &ss) +{ + if (!is_valid(ss)) + throw std::invalid_argument("invalid subdev_spec"); + + return d_db[ss.side][ss.subdev]; +} + bool usrp_basic::start () { @@ -180,22 +235,22 @@ usrp_basic::set_usb_data_rate (int usb_data_rate) } bool -usrp_basic::write_aux_dac (int slot, int which_dac, int value) +usrp_basic::_write_aux_dac (int slot, int which_dac, int value) { return usrp_write_aux_dac (d_udh, slot, which_dac, value); } bool -usrp_basic::read_aux_adc (int slot, int which_adc, int *value) +usrp_basic::_read_aux_adc (int slot, int which_adc, int *value) { return usrp_read_aux_adc (d_udh, slot, which_adc, value); } int -usrp_basic::read_aux_adc (int slot, int which_adc) +usrp_basic::_read_aux_adc (int slot, int which_adc) { int value; - if (!read_aux_adc (slot, which_adc, &value)) + if (!_read_aux_adc (slot, which_adc, &value)) return READ_FAILED; return value; @@ -250,22 +305,22 @@ usrp_basic::serial_number() // ---------------------------------------------------------------- bool -usrp_basic::set_adc_offset (int which, int offset) +usrp_basic::set_adc_offset (int which_adc, int offset) { - if (which < 0 || which > 3) + if (which_adc < 0 || which_adc > 3) return false; - return _write_fpga_reg (FR_ADC_OFFSET_0 + which, offset); + return _write_fpga_reg (FR_ADC_OFFSET_0 + which_adc, offset); } bool -usrp_basic::set_dac_offset (int which, int offset, int offset_pin) +usrp_basic::set_dac_offset (int which_dac, int offset, int offset_pin) { - if (which < 0 || which > 3) + if (which_dac < 0 || which_dac > 3) return false; - int which_codec = which >> 1; - int tx_a = (which & 0x1) == 0; + int which_codec = which_dac >> 1; + int tx_a = (which_dac & 0x1) == 0; int lo = ((offset & 0x3) << 6) | (offset_pin & 0x1); int hi = (offset >> 2); bool ok; @@ -282,13 +337,13 @@ usrp_basic::set_dac_offset (int which, int offset, int offset_pin) } bool -usrp_basic::set_adc_buffer_bypass (int which, bool bypass) +usrp_basic::set_adc_buffer_bypass (int which_adc, bool bypass) { - if (which < 0 || which > 3) + if (which_adc < 0 || which_adc > 3) return false; - int codec = which >> 1; - int reg = (which & 1) == 0 ? REG_RX_A : REG_RX_B; + int codec = which_adc >> 1; + int reg = (which_adc & 1) == 0 ? REG_RX_A : REG_RX_B; unsigned char cur_rx; unsigned char cur_pwr_dn; @@ -302,11 +357,11 @@ usrp_basic::set_adc_buffer_bypass (int which, bool bypass) if (bypass){ cur_rx |= RX_X_BYPASS_INPUT_BUFFER; - cur_pwr_dn |= ((which & 1) == 0) ? RX_PWR_DN_BUF_A : RX_PWR_DN_BUF_B; + cur_pwr_dn |= ((which_adc & 1) == 0) ? RX_PWR_DN_BUF_A : RX_PWR_DN_BUF_B; } else { cur_rx &= ~RX_X_BYPASS_INPUT_BUFFER; - cur_pwr_dn &= ~(((which & 1) == 0) ? RX_PWR_DN_BUF_A : RX_PWR_DN_BUF_B); + cur_pwr_dn &= ~(((which_adc & 1) == 0) ? RX_PWR_DN_BUF_A : RX_PWR_DN_BUF_B); } ok &= _write_9862 (codec, reg, cur_rx); @@ -314,6 +369,13 @@ usrp_basic::set_adc_buffer_bypass (int which, bool bypass) return ok; } +bool +usrp_basic::set_dc_offset_cl_enable(int bits, int mask) +{ + return _write_fpga_reg(FR_DC_OFFSET_CL_EN, + (d_fpga_shadows[FR_DC_OFFSET_CL_EN] & ~mask) | (bits & mask)); +} + // ---------------------------------------------------------------- bool @@ -413,11 +475,278 @@ usrp_basic::_read_spi (int optional_header, int enables, int format, int len) bool -usrp_basic::_set_led (int which, bool on) +usrp_basic::_set_led (int which_led, bool on) +{ + return usrp_set_led (d_udh, which_led, on); +} + +bool +usrp_basic::write_atr_tx_delay(int value) +{ + return _write_fpga_reg(FR_ATR_TX_DELAY, value); +} + +bool +usrp_basic::write_atr_rx_delay(int value) +{ + return _write_fpga_reg(FR_ATR_RX_DELAY, value); +} + +/* + * ---------------------------------------------------------------- + * Routines to access and control daughterboard specific i/o + * ---------------------------------------------------------------- + */ +static int +slot_id_to_oe_reg (int slot_id) +{ + static int reg[4] = { FR_OE_0, FR_OE_1, FR_OE_2, FR_OE_3 }; + assert (0 <= slot_id && slot_id < 4); + return reg[slot_id]; +} + +static int +slot_id_to_io_reg (int slot_id) +{ + static int reg[4] = { FR_IO_0, FR_IO_1, FR_IO_2, FR_IO_3 }; + assert (0 <= slot_id && slot_id < 4); + return reg[slot_id]; +} + +static int +slot_id_to_refclk_reg(int slot_id) +{ + static int reg[4] = { FR_TX_A_REFCLK, FR_RX_A_REFCLK, FR_TX_B_REFCLK, FR_RX_B_REFCLK }; + assert (0 <= slot_id && slot_id < 4); + return reg[slot_id]; +} + +static int +slot_id_to_atr_mask_reg(int slot_id) +{ + static int reg[4] = { FR_ATR_MASK_0, FR_ATR_MASK_1, FR_ATR_MASK_2, FR_ATR_MASK_3 }; + assert (0 <= slot_id && slot_id < 4); + return reg[slot_id]; +} + +static int +slot_id_to_atr_txval_reg(int slot_id) +{ + static int reg[4] = { FR_ATR_TXVAL_0, FR_ATR_TXVAL_1, FR_ATR_TXVAL_2, FR_ATR_TXVAL_3 }; + assert (0 <= slot_id && slot_id < 4); + return reg[slot_id]; +} + +static int +slot_id_to_atr_rxval_reg(int slot_id) +{ + static int reg[4] = { FR_ATR_RXVAL_0, FR_ATR_RXVAL_1, FR_ATR_RXVAL_2, FR_ATR_RXVAL_3 }; + assert (0 <= slot_id && slot_id < 4); + return reg[slot_id]; +} + +static int +to_slot(txrx_t txrx, int which_side) +{ + // TX_A = 0 + // RX_A = 1 + // TX_B = 2 + // RX_B = 3 + return ((which_side & 0x1) << 1) | ((txrx & 0x1) == C_RX); +} + +bool +usrp_basic::common_set_pga(txrx_t txrx, int which_amp, double gain) +{ + if (which_amp < 0 || which_amp > 3) + return false; + + gain = std::min(common_pga_max(txrx), + std::max(common_pga_min(txrx), gain)); + + int codec = which_amp >> 1; + int int_gain = (int) rint((gain - common_pga_min(txrx)) / common_pga_db_per_step(txrx)); + + if (txrx == C_TX){ // 0 and 1 are same, as are 2 and 3 + return _write_9862(codec, REG_TX_PGA, int_gain); + } + else { + int reg = (which_amp & 1) == 0 ? REG_RX_A : REG_RX_B; + + // read current value to get input buffer bypass flag. + unsigned char cur_rx; + if (!_read_9862(codec, reg, &cur_rx)) + return false; + + cur_rx = (cur_rx & RX_X_BYPASS_INPUT_BUFFER) | (int_gain & 0x7f); + return _write_9862(codec, reg, cur_rx); + } +} + +double +usrp_basic::common_pga(txrx_t txrx, int which_amp) const +{ + if (which_amp < 0 || which_amp > 3) + return READ_FAILED; + + if (txrx == C_TX){ + int codec = which_amp >> 1; + unsigned char v; + bool ok = _read_9862 (codec, REG_TX_PGA, &v); + if (!ok) + return READ_FAILED; + + return (pga_db_per_step() * v) + pga_min(); + } + else { + int codec = which_amp >> 1; + int reg = (which_amp & 1) == 0 ? REG_RX_A : REG_RX_B; + unsigned char v; + bool ok = _read_9862 (codec, reg, &v); + if (!ok) + return READ_FAILED; + + return (pga_db_per_step() * (v & 0x1f)) + pga_min(); + } +} + +double +usrp_basic::common_pga_min(txrx_t txrx) const +{ + if (txrx == C_TX) + return -20.0; + else + return 0.0; +} + +double +usrp_basic::common_pga_max(txrx_t txrx) const +{ + if (txrx == C_TX) + return 0.0; + else + return 20.0; +} + +double +usrp_basic::common_pga_db_per_step(txrx_t txrx) const +{ + if (txrx == C_TX) + return 20.0 / 255; + else + return 20.0 / 20; +} + +bool +usrp_basic::_common_write_oe(txrx_t txrx, int which_side, int value, int mask) +{ + if (! (0 <= which_side && which_side <= 1)) + return false; + + return _write_fpga_reg(slot_id_to_oe_reg(to_slot(txrx, which_side)), + (mask << 16) | (value & 0xffff)); +} + +bool +usrp_basic::common_write_io(txrx_t txrx, int which_side, int value, int mask) +{ + if (! (0 <= which_side && which_side <= 1)) + return false; + + return _write_fpga_reg(slot_id_to_io_reg(to_slot(txrx, which_side)), + (mask << 16) | (value & 0xffff)); +} + +bool +usrp_basic::common_read_io(txrx_t txrx, int which_side, int *value) +{ + if (! (0 <= which_side && which_side <= 1)) + return false; + + int t; + int reg = which_side + 1; // FIXME, *very* magic number (fix in serial_io.v) + bool ok = _read_fpga_reg(reg, &t); + if (!ok) + return false; + + if (txrx == C_TX){ + *value = t & 0xffff; // FIXME, more magic + return true; + } + else { + *value = (t >> 16) & 0xffff; // FIXME, more magic + return true; + } +} + +int +usrp_basic::common_read_io(txrx_t txrx, int which_side) +{ + int value; + if (!common_read_io(txrx, which_side, &value)) + return READ_FAILED; + return value; +} + +bool +usrp_basic::common_write_refclk(txrx_t txrx, int which_side, int value) +{ + if (! (0 <= which_side && which_side <= 1)) + return false; + + return _write_fpga_reg(slot_id_to_refclk_reg(to_slot(txrx, which_side)), + value); +} + +bool +usrp_basic::common_write_atr_mask(txrx_t txrx, int which_side, int value) +{ + if (! (0 <= which_side && which_side <= 1)) + return false; + + return _write_fpga_reg(slot_id_to_atr_mask_reg(to_slot(txrx, which_side)), + value); +} + +bool +usrp_basic::common_write_atr_txval(txrx_t txrx, int which_side, int value) +{ + if (! (0 <= which_side && which_side <= 1)) + return false; + + return _write_fpga_reg(slot_id_to_atr_txval_reg(to_slot(txrx, which_side)), + value); +} + +bool +usrp_basic::common_write_atr_rxval(txrx_t txrx, int which_side, int value) +{ + if (! (0 <= which_side && which_side <= 1)) + return false; + + return _write_fpga_reg(slot_id_to_atr_rxval_reg(to_slot(txrx, which_side)), + value); +} + +bool +usrp_basic::common_write_aux_dac(txrx_t txrx, int which_side, int which_dac, int value) { - return usrp_set_led (d_udh, which, on); + return _write_aux_dac(to_slot(txrx, which_side), which_dac, value); } +bool +usrp_basic::common_read_aux_adc(txrx_t txrx, int which_side, int which_adc, int *value) +{ + return _read_aux_adc(to_slot(txrx, which_side), which_adc, value); +} + +int +usrp_basic::common_read_aux_adc(txrx_t txrx, int which_side, int which_adc) +{ + return _read_aux_adc(to_slot(txrx, which_side), which_adc); +} + + //////////////////////////////////////////////////////////////// // // usrp_basic_rx @@ -467,6 +796,9 @@ usrp_basic_rx::usrp_basic_rx (int which_board, int fusb_block_size, int fusb_nbl probe_rx_slots (false); + //d_db[0] = instantiate_dbs(d_dbid[0], this, 0); + //d_db[1] = instantiate_dbs(d_dbid[1], this, 1); + // check fusb buffering parameters if (fusb_block_size < 0 || fusb_block_size > FUSB_BLOCK_SIZE) @@ -485,12 +817,12 @@ usrp_basic_rx::usrp_basic_rx (int which_board, int fusb_block_size, int fusb_nbl d_ephandle = d_devhandle->make_ephandle (USRP_RX_ENDPOINT, true, fusb_block_size, fusb_nblocks); - _write_fpga_reg(FR_ATR_MASK_1, 0); // zero Rx side Auto Transmit/Receive regs - _write_fpga_reg(FR_ATR_TXVAL_1, 0); - _write_fpga_reg(FR_ATR_RXVAL_1, 0); - _write_fpga_reg(FR_ATR_MASK_3, 0); - _write_fpga_reg(FR_ATR_TXVAL_3, 0); - _write_fpga_reg(FR_ATR_RXVAL_3, 0); + write_atr_mask(0, 0); // zero Rx A Auto Transmit/Receive regs + write_atr_txval(0, 0); + write_atr_rxval(0, 0); + write_atr_mask(1, 0); // zero Rx B Auto Transmit/Receive regs + write_atr_txval(1, 0); + write_atr_rxval(1, 0); } static unsigned char rx_fini_regs[] = { @@ -511,6 +843,8 @@ usrp_basic_rx::~usrp_basic_rx () if (!usrp_9862_write_many_all (d_udh, rx_fini_regs, sizeof (rx_fini_regs))){ fprintf (stderr, "usrp_basic_rx: failed to fini AD9862 RX regs\n"); } + + shutdown_daughterboards(); } @@ -656,61 +990,6 @@ usrp_basic_rx::restore_rx (bool on) set_rx_enable (on); } -bool -usrp_basic_rx::set_pga (int which, double gain) -{ - if (which < 0 || which > 3) - return false; - - gain = std::max (pga_min (), gain); - gain = std::min (pga_max (), gain); - - int codec = which >> 1; - int reg = (which & 1) == 0 ? REG_RX_A : REG_RX_B; - - // read current value to get input buffer bypass flag. - unsigned char cur_rx; - if (!_read_9862 (codec, reg, &cur_rx)) - return false; - - int int_gain = (int) rint ((gain - pga_min ()) / pga_db_per_step()); - - cur_rx = (cur_rx & RX_X_BYPASS_INPUT_BUFFER) | (int_gain & 0x7f); - return _write_9862 (codec, reg, cur_rx); -} - -double -usrp_basic_rx::pga (int which) const -{ - if (which < 0 || which > 3) - return READ_FAILED; - - int codec = which >> 1; - int reg = (which & 1) == 0 ? REG_RX_A : REG_RX_B; - unsigned char v; - bool ok = _read_9862 (codec, reg, &v); - if (!ok) - return READ_FAILED; - - return (pga_db_per_step() * (v & 0x1f)) + pga_min(); -} - -static int -slot_id_to_oe_reg (int slot_id) -{ - static int reg[4] = { FR_OE_0, FR_OE_1, FR_OE_2, FR_OE_3 }; - assert (0 <= slot_id && slot_id < 4); - return reg[slot_id]; -} - -static int -slot_id_to_io_reg (int slot_id) -{ - static int reg[4] = { FR_IO_0, FR_IO_1, FR_IO_2, FR_IO_3 }; - assert (0 <= slot_id && slot_id < 4); - return reg[slot_id]; -} - void usrp_basic_rx::probe_rx_slots (bool verbose) { @@ -760,80 +1039,104 @@ usrp_basic_rx::probe_rx_slots (bool verbose) } bool -usrp_basic_rx::_write_oe (int which_dboard, int value, int mask) +usrp_basic_rx::set_pga (int which_amp, double gain) { - if (! (0 <= which_dboard && which_dboard <= 1)) - return false; + return common_set_pga(C_RX, which_amp, gain); +} - return _write_fpga_reg (slot_id_to_oe_reg (dboard_to_slot (which_dboard)), - (mask << 16) | (value & 0xffff)); +double +usrp_basic_rx::pga(int which_amp) const +{ + return common_pga(C_RX, which_amp); } -bool -usrp_basic_rx::write_io (int which_dboard, int value, int mask) +double +usrp_basic_rx::pga_min() const { - if (! (0 <= which_dboard && which_dboard <= 1)) - return false; + return common_pga_min(C_RX); +} - return _write_fpga_reg (slot_id_to_io_reg (dboard_to_slot (which_dboard)), - (mask << 16) | (value & 0xffff)); +double +usrp_basic_rx::pga_max() const +{ + return common_pga_max(C_RX); +} + +double +usrp_basic_rx::pga_db_per_step() const +{ + return common_pga_db_per_step(C_RX); } bool -usrp_basic_rx::read_io (int which_dboard, int *value) +usrp_basic_rx::_write_oe (int which_side, int value, int mask) { - if (! (0 <= which_dboard && which_dboard <= 1)) - return false; + return _common_write_oe(C_RX, which_side, value, mask); +} - int t; - int reg = which_dboard + 1; // FIXME, *very* magic number (fix in serial_io.v) - bool ok = _read_fpga_reg (reg, &t); - if (!ok) - return false; +bool +usrp_basic_rx::write_io (int which_side, int value, int mask) +{ + return common_write_io(C_RX, which_side, value, mask); +} - *value = (t >> 16) & 0xffff; // FIXME, more magic - return true; +bool +usrp_basic_rx::read_io (int which_side, int *value) +{ + return common_read_io(C_RX, which_side, value); } int -usrp_basic_rx::read_io (int which_dboard) +usrp_basic_rx::read_io (int which_side) { - int value; - if (!read_io (which_dboard, &value)) - return READ_FAILED; - return value; + return common_read_io(C_RX, which_side); } bool -usrp_basic_rx::write_aux_dac (int which_dboard, int which_dac, int value) +usrp_basic_rx::write_refclk(int which_side, int value) { - return usrp_basic::write_aux_dac (dboard_to_slot (which_dboard), - which_dac, value); + return common_write_refclk(C_RX, which_side, value); } bool -usrp_basic_rx::read_aux_adc (int which_dboard, int which_adc, int *value) +usrp_basic_rx::write_atr_mask(int which_side, int value) { - return usrp_basic::read_aux_adc (dboard_to_slot (which_dboard), - which_adc, value); + return common_write_atr_mask(C_RX, which_side, value); } -int -usrp_basic_rx::read_aux_adc (int which_dboard, int which_adc) +bool +usrp_basic_rx::write_atr_txval(int which_side, int value) { - return usrp_basic::read_aux_adc (dboard_to_slot (which_dboard), which_adc); + return common_write_atr_txval(C_RX, which_side, value); } -int -usrp_basic_rx::block_size () const { return d_ephandle->block_size(); } +bool +usrp_basic_rx::write_atr_rxval(int which_side, int value) +{ + return common_write_atr_rxval(C_RX, which_side, value); +} bool -usrp_basic_rx::set_dc_offset_cl_enable(int bits, int mask) +usrp_basic_rx::write_aux_dac (int which_side, int which_dac, int value) { - return _write_fpga_reg(FR_DC_OFFSET_CL_EN, - (d_fpga_shadows[FR_DC_OFFSET_CL_EN] & ~mask) | (bits & mask)); + return common_write_aux_dac(C_RX, which_side, which_dac, value); +} + +bool +usrp_basic_rx::read_aux_adc (int which_side, int which_adc, int *value) +{ + return common_read_aux_adc(C_RX, which_side, which_adc, value); } +int +usrp_basic_rx::read_aux_adc (int which_side, int which_adc) +{ + return common_read_aux_adc(C_RX, which_side, which_adc); +} + +int +usrp_basic_rx::block_size () const { return d_ephandle->block_size(); } + //////////////////////////////////////////////////////////////// // // usrp_basic_tx @@ -902,6 +1205,9 @@ usrp_basic_tx::usrp_basic_tx (int which_board, int fusb_block_size, int fusb_nbl probe_tx_slots (false); + //d_db[0] = instantiate_dbs(d_dbid[0], this, 0); + //d_db[1] = instantiate_dbs(d_dbid[1], this, 1); + // check fusb buffering parameters if (fusb_block_size < 0 || fusb_block_size > FUSB_BLOCK_SIZE) @@ -920,12 +1226,12 @@ usrp_basic_tx::usrp_basic_tx (int which_board, int fusb_block_size, int fusb_nbl d_ephandle = d_devhandle->make_ephandle (USRP_TX_ENDPOINT, false, fusb_block_size, fusb_nblocks); - _write_fpga_reg(FR_ATR_MASK_0, 0); // zero Tx side Auto Transmit/Receive regs - _write_fpga_reg(FR_ATR_TXVAL_0, 0); - _write_fpga_reg(FR_ATR_RXVAL_0, 0); - _write_fpga_reg(FR_ATR_MASK_2, 0); - _write_fpga_reg(FR_ATR_TXVAL_2, 0); - _write_fpga_reg(FR_ATR_RXVAL_2, 0); + write_atr_mask(0, 0); // zero Tx A Auto Transmit/Receive regs + write_atr_txval(0, 0); + write_atr_rxval(0, 0); + write_atr_mask(1, 0); // zero Tx B Auto Transmit/Receive regs + write_atr_txval(1, 0); + write_atr_rxval(1, 0); } @@ -945,6 +1251,8 @@ usrp_basic_tx::~usrp_basic_tx () if (!usrp_9862_write_many_all (d_udh, tx_fini_regs, sizeof (tx_fini_regs))){ fprintf (stderr, "usrp_basic_tx: failed to fini AD9862 TX regs\n"); } + + shutdown_daughterboards(); } bool @@ -1094,37 +1402,6 @@ usrp_basic_tx::restore_tx (bool on) set_tx_enable (on); } -bool -usrp_basic_tx::set_pga (int which, double gain) -{ - if (which < 0 || which > 3) - return false; - - gain = std::max (pga_min (), gain); - gain = std::min (pga_max (), gain); - - int codec = which >> 1; // 0 and 1 are same, as are 2 and 3 - - int int_gain = (int) rint ((gain - pga_min ()) / pga_db_per_step()); - - return _write_9862 (codec, REG_TX_PGA, int_gain); -} - -double -usrp_basic_tx::pga (int which) const -{ - if (which < 0 || which > 3) - return READ_FAILED; - - int codec = which >> 1; - unsigned char v; - bool ok = _read_9862 (codec, REG_TX_PGA, &v); - if (!ok) - return READ_FAILED; - - return (pga_db_per_step() * v) + pga_min(); -} - void usrp_basic_tx::probe_tx_slots (bool verbose) { @@ -1174,68 +1451,99 @@ usrp_basic_tx::probe_tx_slots (bool verbose) } bool -usrp_basic_tx::_write_oe (int which_dboard, int value, int mask) +usrp_basic_tx::set_pga (int which_amp, double gain) { - if (! (0 <= which_dboard && which_dboard <= 1)) - return false; + return common_set_pga(C_TX, which_amp, gain); +} - return _write_fpga_reg (slot_id_to_oe_reg (dboard_to_slot (which_dboard)), - (mask << 16) | (value & 0xffff)); +double +usrp_basic_tx::pga (int which_amp) const +{ + return common_pga(C_TX, which_amp); } -bool -usrp_basic_tx::write_io (int which_dboard, int value, int mask) +double +usrp_basic_tx::pga_min() const { - if (! (0 <= which_dboard && which_dboard <= 1)) - return false; + return common_pga_min(C_TX); +} - return _write_fpga_reg (slot_id_to_io_reg (dboard_to_slot (which_dboard)), - (mask << 16) | (value & 0xffff)); +double +usrp_basic_tx::pga_max() const +{ + return common_pga_max(C_TX); +} + +double +usrp_basic_tx::pga_db_per_step() const +{ + return common_pga_db_per_step(C_TX); } bool -usrp_basic_tx::read_io (int which_dboard, int *value) +usrp_basic_tx::_write_oe (int which_side, int value, int mask) { - if (! (0 <= which_dboard && which_dboard <= 1)) - return false; + return _common_write_oe(C_TX, which_side, value, mask); +} - int t; - int reg = which_dboard + 1; // FIXME, *very* magic number (fix in serial_io.v) - bool ok = _read_fpga_reg (reg, &t); - if (!ok) - return false; +bool +usrp_basic_tx::write_io (int which_side, int value, int mask) +{ + return common_write_io(C_TX, which_side, value, mask); +} - *value = t & 0xffff; // FIXME, more magic - return true; +bool +usrp_basic_tx::read_io (int which_side, int *value) +{ + return common_read_io(C_TX, which_side, value); } int -usrp_basic_tx::read_io (int which_dboard) +usrp_basic_tx::read_io (int which_side) { - int value; - if (!read_io (which_dboard, &value)) - return READ_FAILED; - return value; + return common_read_io(C_TX, which_side); +} + +bool +usrp_basic_tx::write_refclk(int which_side, int value) +{ + return common_write_refclk(C_TX, which_side, value); +} + +bool +usrp_basic_tx::write_atr_mask(int which_side, int value) +{ + return common_write_atr_mask(C_TX, which_side, value); +} + +bool +usrp_basic_tx::write_atr_txval(int which_side, int value) +{ + return common_write_atr_txval(C_TX, which_side, value); +} + +bool +usrp_basic_tx::write_atr_rxval(int which_side, int value) +{ + return common_write_atr_rxval(C_TX, which_side, value); } bool -usrp_basic_tx::write_aux_dac (int which_dboard, int which_dac, int value) +usrp_basic_tx::write_aux_dac (int which_side, int which_dac, int value) { - return usrp_basic::write_aux_dac (dboard_to_slot (which_dboard), - which_dac, value); + return common_write_aux_dac(C_TX, which_side, which_dac, value); } bool -usrp_basic_tx::read_aux_adc (int which_dboard, int which_adc, int *value) +usrp_basic_tx::read_aux_adc (int which_side, int which_adc, int *value) { - return usrp_basic::read_aux_adc (dboard_to_slot (which_dboard), - which_adc, value); + return common_read_aux_adc(C_TX, which_side, which_adc, value); } int -usrp_basic_tx::read_aux_adc (int which_dboard, int which_adc) +usrp_basic_tx::read_aux_adc (int which_side, int which_adc) { - return usrp_basic::read_aux_adc (dboard_to_slot (which_dboard), which_adc); + return common_read_aux_adc(C_TX, which_side, which_adc); } int diff --git a/usrp/host/lib/legacy/usrp_basic.h b/usrp/host/lib/legacy/usrp_basic.h index 395a1dac0..c5e3d2824 100644 --- a/usrp/host/lib/legacy/usrp_basic.h +++ b/usrp/host/lib/legacy/usrp_basic.h @@ -1,6 +1,7 @@ + /* -*- c++ -*- */ /* - * Copyright 2003,2004 Free Software Foundation, Inc. + * Copyright 2003,2004,2008 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -39,24 +40,30 @@ #ifndef INCLUDED_USRP_BASIC_H #define INCLUDED_USRP_BASIC_H +#include <db_base.h> #include <usrp_slots.h> #include <string> +#include <vector> +#include <boost/utility.hpp> +#include <usrp_subdev_spec.h> struct usb_dev_handle; class fusb_devhandle; class fusb_ephandle; +enum txrx_t { + C_RX = 0, + C_TX = 1 +}; + /*! - * \brief base class for usrp operations + * \brief abstract base class for usrp operations */ -class usrp_basic +class usrp_basic : boost::noncopyable { -private: - // NOT IMPLEMENTED - usrp_basic (const usrp_basic &rhs); // no copy constructor - usrp_basic &operator= (const usrp_basic &rhs); // no assignment operator +protected: + void shutdown_daughterboards(); - protected: struct usb_dev_handle *d_udh; int d_usb_data_rate; // bytes/sec @@ -66,6 +73,22 @@ protected: static const int MAX_REGS = 128; unsigned int d_fpga_shadows[MAX_REGS]; + int d_dbid[2]; // daughterboard ID's (side A, side B) + + /*! + * Shared pointers to subclasses of db_base. + * + * The outer vector is of length 2 (0 = side A, 1 = side B). The + * inner vectors are of length 1, 2 or 3 depending on the number of + * subdevices implemented by the daugherboard. At this time, only + * the Basic Rx and LF Rx implement more than 1 subdevice. + */ + std::vector< std::vector<db_base_sptr> > d_db; + + //! One time call, made only only from usrp_standard_*::make after shared_ptr is created. + void init_db(usrp_basic_sptr u); + + usrp_basic (int which_board, struct usb_dev_handle *open_interface (struct usb_device *dev), const std::string fpga_filename = "", @@ -92,7 +115,7 @@ protected: * \param value [0,4095] * \returns true iff successful */ - bool write_aux_dac (int slot, int which_dac, int value); + bool _write_aux_dac (int slot, int which_dac, int value); /*! * \brief Read auxiliary analog to digital converter. @@ -102,7 +125,7 @@ protected: * \param value return 12-bit value [0,4095] * \returns true iff successful */ - bool read_aux_adc (int slot, int which_adc, int *value); + bool _read_aux_adc (int slot, int which_adc, int *value); /*! * \brief Read auxiliary analog to digital converter. @@ -111,11 +134,46 @@ protected: * \param which_adc [0,1] * \returns value in the range [0,4095] if successful, else READ_FAILED. */ - int read_aux_adc (int slot, int which_adc); + int _read_aux_adc (int slot, int which_adc); + public: virtual ~usrp_basic (); + + /*! + * Return a vector of vectors that contain shared pointers + * to the daughterboard instance(s) associated with the specified side. + * + * It is an error to use the returned objects after the usrp_basic + * object has been destroyed. + */ + std::vector<std::vector<db_base_sptr> > db() const { return d_db; } + + /*! + * Return a vector of size >= 1 that contains shared pointers + * to the daughterboard instance(s) associated with the specified side. + * + * \param which_side [0,1] which daughterboard + * + * It is an error to use the returned objects after the usrp_basic + * object has been destroyed. + */ + std::vector<db_base_sptr> db(int which_side); + + /*! + * \brief is the subdev_spec valid? + */ + bool is_valid(const usrp_subdev_spec &ss); + + /*! + * \brief given a subdev_spec, return the corresponding daughterboard object. + * \throws std::invalid_ argument if ss is invalid. + * + * \param ss specifies the side and subdevice + */ + db_base_sptr selected_subdev(const usrp_subdev_spec &ss); + /*! * \brief return frequency of master oscillator on USRP */ @@ -172,7 +230,7 @@ public: * \param which which ADC[0,3]: 0 = RX_A I, 1 = RX_A Q... * \param offset 16-bit value to subtract from raw ADC input. */ - bool set_adc_offset (int which, int offset); + bool set_adc_offset (int which_adc, int offset); /*! * \brief Set DAC offset correction @@ -181,16 +239,37 @@ public: * \param offset_pin 1-bit value. If 0 offset applied to -ve differential pin; * If 1 offset applied to +ve differential pin. */ - bool set_dac_offset (int which, int offset, int offset_pin); + bool set_dac_offset (int which_dac, int offset, int offset_pin); /*! * \brief Control ADC input buffer - * \param which which ADC[0,3] + * \param which_adc which ADC[0,3] * \param bypass if non-zero, bypass input buffer and connect input * directly to switched cap SHA input of RxPGA. */ - bool set_adc_buffer_bypass (int which, bool bypass); + bool set_adc_buffer_bypass (int which_adc, bool bypass); + /*! + * \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. + * + * <pre> + * The 4 low bits are significant: + * + * ADC0 = (1 << 0) + * ADC1 = (1 << 1) + * ADC2 = (1 << 2) + * ADC3 = (1 << 3) + * </pre> + * + * By default the control loop is enabled on all ADC's. + */ + bool set_dc_offset_cl_enable(int bits, int mask); /*! * \brief return the usrp's serial number. @@ -199,12 +278,376 @@ public: */ std::string serial_number(); + /*! + * \brief Return daughterboard ID for given side [0,1]. + * + * \param which_side [0,1] which daughterboard + * + * \return daughterboard id >= 0 if successful + * \return -1 if no daugherboard + * \return -2 if invalid EEPROM on daughterboard + */ + virtual int daughterboard_id (int which_side) const = 0; + + /*! + * \brief Clock ticks to delay rising of T/R signal + * \sa write_atr_mask, write_atr_txval, write_atr_rxval + */ + bool write_atr_tx_delay(int value); + + /*! + * \brief Clock ticks to delay falling edge of T/R signal + * \sa write_atr_mask, write_atr_txval, write_atr_rxval + */ + bool write_atr_rx_delay(int value); + + + // ================================================================ + // Routines to access and control daughterboard specific i/o + // + // Those with a common_ prefix access either the Tx or Rx side depending + // on the txrx parameter. Those without the common_ prefix are virtual + // and are overriden in usrp_basic_rx and usrp_basic_tx to access the + // the Rx or Tx sides automatically. We provide the common_ versions + // for those daughterboards such as the WBX and XCVR2450 that share + // h/w resources (such as the LO) between the Tx and Rx sides. + + // ---------------------------------------------------------------- + // BEGIN common_ daughterboard control functions + + /*! + * \brief Set Programmable Gain Amplifier(PGA) + * + * \param txrx Tx or Rx? + * \param which_amp which amp [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 common_set_pga(txrx_t txrx, int which_amp, double gain_in_db); + + /*! + * \brief Return programmable gain amplifier gain setting in dB. + * + * \param txrx Tx or Rx? + * \param which_amp which amp [0,3] + */ + double common_pga(txrx_t txrx, int which_amp) const; + + /*! + * \brief Return minimum legal PGA gain in dB. + * \param txrx Tx or Rx? + */ + double common_pga_min(txrx_t txrx) const; + + /*! + * \brief Return maximum legal PGA gain in dB. + * \param txrx Tx or Rx? + */ + double common_pga_max(txrx_t txrx) const; + + /*! + * \brief Return hardware step size of PGA(linear in dB). + * \param txrx Tx or Rx? + */ + double common_pga_db_per_step(txrx_t txrx) const; + + /*! + * \brief Write direction register(output enables) for pins that go to daughterboard. + * + * \param txrx Tx or Rx? + * \param which_side [0,1] which size + * \param value value to write into register + * \param mask which bits of value to write into reg + * + * Each d'board has 16-bits of general purpose i/o. + * Setting the bit makes it an output from the FPGA to the d'board. + * + * This register is initialized based on a value stored in the + * d'board EEPROM. In general, you shouldn't be using this routine + * without a very good reason. Using this method incorrectly will + * kill your USRP motherboard and/or daughterboard. + */ + bool _common_write_oe(txrx_t txrx, int which_side, int value, int mask); + + /*! + * \brief Write daughterboard i/o pin value + * + * \param txrx Tx or Rx? + * \param which_side [0,1] which d'board + * \param value value to write into register + * \param mask which bits of value to write into reg + */ + bool common_write_io(txrx_t txrx, int which_side, int value, int mask); + + /*! + * \brief Read daughterboard i/o pin value + * + * \param txrx Tx or Rx? + * \param which_side [0,1] which d'board + * \param value output + */ + bool common_read_io(txrx_t txrx, int which_side, int *value); + + /*! + * \brief Read daughterboard i/o pin value + * + * \param txrx Tx or Rx? + * \param which_side [0,1] which d'board + * \returns register value if successful, else READ_FAILED + */ + int common_read_io(txrx_t txrx, int which_side); + + /*! + * \brief Write daughterboard refclk config register + * + * \param txrx Tx or Rx? + * \param which_side [0,1] which d'board + * \param value value to write into register, see below + * + * <pre> + * Control whether a reference clock is sent to the daughterboards, + * and what frequency. The refclk is sent on d'board i/o pin 0. + * + * 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) |E| DIVISOR | + * +-----------------------------------------------+-+------------+ + * + * Bit 7 -- 1 turns on refclk, 0 allows IO use + * Bits 6:0 Divider value + * </pre> + */ + bool common_write_refclk(txrx_t txrx, int which_side, int value); + + /*! + * \brief Automatic Transmit/Receive switching + * <pre> + * + * If automatic transmit/receive (ATR) switching is enabled in the + * FR_ATR_CTL register, the presence or absence of data in the FPGA + * transmit fifo selects between two sets of values for each of the 4 + * banks of daughterboard i/o pins. + * + * Each daughterboard slot has 3 16-bit registers associated with it: + * FR_ATR_MASK_*, FR_ATR_TXVAL_* and FR_ATR_RXVAL_* + * + * FR_ATR_MASK_{0,1,2,3}: + * + * These registers determine which of the daugherboard i/o pins are + * affected by ATR switching. If a bit in the mask is set, the + * corresponding i/o bit is controlled by ATR, else it's output + * value comes from the normal i/o pin output register: + * FR_IO_{0,1,2,3}. + * + * FR_ATR_TXVAL_{0,1,2,3}: + * FR_ATR_RXVAL_{0,1,2,3}: + * + * If the Tx fifo contains data, then the bits from TXVAL that are + * selected by MASK are output. Otherwise, the bits from RXVAL that + * are selected by MASK are output. + * </pre> + */ + bool common_write_atr_mask(txrx_t txrx, int which_side, int value); + bool common_write_atr_txval(txrx_t txrx, int which_side, int value); + bool common_write_atr_rxval(txrx_t txrx, int which_side, int value); + + /*! + * \brief Write auxiliary digital to analog converter. + * + * \param txrx Tx or Rx? + * \param which_side [0,1] which d'board + * N.B., SLOT_TX_A and SLOT_RX_A share the same AUX DAC's. + * SLOT_TX_B and SLOT_RX_B share the same AUX DAC's. + * \param which_dac [2,3] TX slots must use only 2 and 3. + * \param value [0,4095] + * \returns true iff successful + */ + bool common_write_aux_dac(txrx_t txrx, int which_side, int which_dac, int value); + + /*! + * \brief Read auxiliary analog to digital converter. + * + * \param txrx Tx or Rx? + * \param which_side [0,1] which d'board + * \param which_adc [0,1] + * \param value return 12-bit value [0,4095] + * \returns true iff successful + */ + bool common_read_aux_adc(txrx_t txrx, int which_side, int which_adc, int *value); + + /*! + * \brief Read auxiliary analog to digital converter. + * + * \param txrx Tx or Rx? + * \param which_side [0,1] which d'board + * \param which_adc [0,1] + * \returns value in the range [0,4095] if successful, else READ_FAILED. + */ + int common_read_aux_adc(txrx_t txrx, int which_side, int which_adc); + + // END common_ daughterboard control functions + // ---------------------------------------------------------------- + // BEGIN virtual daughterboard control functions + + /*! + * \brief Set Programmable Gain Amplifier (PGA) + * + * \param which_amp which amp [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() + */ + virtual bool set_pga (int which_amp, double gain_in_db) = 0; + + /*! + * \brief Return programmable gain amplifier gain setting in dB. + * + * \param which_amp which amp [0,3] + */ + virtual double pga (int which_amp) const = 0; + + /*! + * \brief Return minimum legal PGA gain in dB. + */ + virtual double pga_min () const = 0; + + /*! + * \brief Return maximum legal PGA gain in dB. + */ + virtual double pga_max () const = 0; + + /*! + * \brief Return hardware step size of PGA (linear in dB). + */ + virtual double pga_db_per_step () const = 0; + + /*! + * \brief Write direction register (output enables) for pins that go to daughterboard. + * + * \param which_side [0,1] which size + * \param value value to write into register + * \param mask which bits of value to write into reg + * + * Each d'board has 16-bits of general purpose i/o. + * Setting the bit makes it an output from the FPGA to the d'board. + * + * This register is initialized based on a value stored in the + * d'board EEPROM. In general, you shouldn't be using this routine + * without a very good reason. Using this method incorrectly will + * kill your USRP motherboard and/or daughterboard. + */ + virtual bool _write_oe (int which_side, int value, int mask) = 0; + + /*! + * \brief Write daughterboard i/o pin value + * + * \param which_side [0,1] which d'board + * \param value value to write into register + * \param mask which bits of value to write into reg + */ + virtual bool write_io (int which_side, int value, int mask) = 0; + + /*! + * \brief Read daughterboard i/o pin value + * + * \param which_side [0,1] which d'board + * \param value output + */ + virtual bool read_io (int which_side, int *value) = 0; + + /*! + * \brief Read daughterboard i/o pin value + * + * \param which_side [0,1] which d'board + * \returns register value if successful, else READ_FAILED + */ + virtual int read_io (int which_side) = 0; + + /*! + * \brief Write daughterboard refclk config register + * + * \param which_side [0,1] which d'board + * \param value value to write into register, see below + * + * <pre> + * Control whether a reference clock is sent to the daughterboards, + * and what frequency. The refclk is sent on d'board i/o pin 0. + * + * 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) |E| DIVISOR | + * +-----------------------------------------------+-+------------+ + * + * Bit 7 -- 1 turns on refclk, 0 allows IO use + * Bits 6:0 Divider value + * </pre> + */ + virtual bool write_refclk(int which_side, int value) = 0; + + virtual bool write_atr_mask(int which_side, int value) = 0; + virtual bool write_atr_txval(int which_side, int value) = 0; + virtual bool write_atr_rxval(int which_side, int value) = 0; + + /*! + * \brief Write auxiliary digital to analog converter. + * + * \param which_side [0,1] which d'board + * N.B., SLOT_TX_A and SLOT_RX_A share the same AUX DAC's. + * SLOT_TX_B and SLOT_RX_B share the same AUX DAC's. + * \param which_dac [2,3] TX slots must use only 2 and 3. + * \param value [0,4095] + * \returns true iff successful + */ + virtual bool write_aux_dac (int which_side, int which_dac, int value) = 0; + + /*! + * \brief Read auxiliary analog to digital converter. + * + * \param which_side [0,1] which d'board + * \param which_adc [0,1] + * \param value return 12-bit value [0,4095] + * \returns true iff successful + */ + virtual bool read_aux_adc (int which_side, int which_adc, int *value) = 0; + + /*! + * \brief Read auxiliary analog to digital converter. + * + * \param which_side [0,1] which d'board + * \param which_adc [0,1] + * \returns value in the range [0,4095] if successful, else READ_FAILED. + */ + virtual int read_aux_adc (int which_side, int which_adc) = 0; + + /*! + * \brief returns current fusb block size + */ + virtual int block_size() const = 0; + + /*! + * \brief returns A/D or D/A converter rate in Hz + */ + virtual long converter_rate() const = 0; + + // END virtual daughterboard control functions + // ---------------------------------------------------------------- // Low level implementation routines. // You probably shouldn't be using these... // - bool _set_led (int which, bool on); + bool _set_led (int which_led, bool on); /*! * \brief Write FPGA register. @@ -229,7 +672,6 @@ public: */ int _read_fpga_reg (int regno); - /*! * \brief Write FPGA register with mask. * \param regno 7-bit register number @@ -324,8 +766,6 @@ private: bool d_rx_enable; protected: - int d_dbid[2]; // Rx daughterboard ID's - /*! * \param which_board Which USRP board on usb (not particularly useful; use 0) * \param fusb_block_size fast usb xfer block size. Must be a multiple of 512. @@ -346,7 +786,6 @@ protected: void restore_rx (bool on); // conditional set void probe_rx_slots (bool verbose); - int dboard_to_slot (int dboard) { return (dboard << 1) | 1; } public: ~usrp_basic_rx (); @@ -358,6 +797,8 @@ public: * \param fusb_block_size fast usb xfer block size. Must be a multiple of 512. * Use zero for a reasonable default. * \param fusb_nblocks number of fast usb URBs to allocate. Use zero for a reasonable default. + * \param fpga_filename name of file that contains image to load into FPGA + * \param firmware_filename name of file that contains image to load into FX2 */ static usrp_basic_rx *make (int which_board, int fusb_block_size=0, @@ -366,8 +807,6 @@ public: const std::string firmware_filename = "" ); - // MANIPULATORS - /*! * \brief tell the fpga the rate rx samples are coming from the A/D's * @@ -389,162 +828,33 @@ public: */ int read (void *buf, int len, bool *overrun); - // ACCESSORS //! sampling rate of A/D converter virtual long converter_rate() const { return fpga_master_clock_freq(); } // 64M long adc_rate() const { return converter_rate(); } - long adc_freq() const { return converter_rate(); } //!< deprecated method name - - /*! - * \brief Return daughterboard ID for given Rx daughterboard slot [0,1]. - * - * \param which_dboard [0,1] which Rx daughterboard - * - * \return daughterboard id >= 0 if successful - * \return -1 if no daugherboard - * \return -2 if invalid EEPROM on daughterboard - */ - int daughterboard_id (int which_dboard) const { return d_dbid[which_dboard & 0x1]; } - - // ---------------------------------------------------------------- - // routines for controlling the Programmable Gain Amplifier - /*! - * \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 gain in dB. - */ - double pga_min () const { return 0.0; } - - /*! - * \brief Return maximum legal PGA gain in dB. - */ - double pga_max () const { return 20.0; } - - /*! - * \brief Return hardware step size of PGA (linear in dB). - */ - double pga_db_per_step () const { return 20.0 / 20; } - - /*! - * \brief Write direction register (output enables) for pins that go to daughterboard. - * - * \param which_dboard [0,1] which d'board - * \param value value to write into register - * \param mask which bits of value to write into reg - * - * Each d'board has 16-bits of general purpose i/o. - * Setting the bit makes it an output from the FPGA to the d'board. - * - * This register is initialized based on a value stored in the - * d'board EEPROM. In general, you shouldn't be using this routine - * without a very good reason. Using this method incorrectly will - * kill your USRP motherboard and/or daughterboard. - */ - bool _write_oe (int which_dboard, int value, int mask); - - /*! - * \brief Write daughterboard i/o pin value - * - * \param which_dboard [0,1] which d'board - * \param value value to write into register - * \param mask which bits of value to write into reg - */ - bool write_io (int which_dboard, int value, int mask); - - /*! - * \brief Read daughterboard i/o pin value - * - * \param which_dboard [0,1] which d'board - * \param value output - */ - bool read_io (int which_dboard, int *value); - - /*! - * \brief Read daughterboard i/o pin value - * - * \param which_dboard [0,1] which d'board - * \returns register value if successful, else READ_FAILED - */ - int read_io (int which_dboard); - - /*! - * \brief Write auxiliary digital to analog converter. - * - * \param which_dboard [0,1] which d'board - * N.B., SLOT_TX_A and SLOT_RX_A share the same AUX DAC's. - * SLOT_TX_B and SLOT_RX_B share the same AUX DAC's. - * \param which_dac [2,3] TX slots must use only 2 and 3. - * \param value [0,4095] - * \returns true iff successful - */ - bool write_aux_dac (int which_board, int which_dac, int value); - - /*! - * \brief Read auxiliary analog to digital converter. - * - * \param which_dboard [0,1] which d'board - * \param which_adc [0,1] - * \param value return 12-bit value [0,4095] - * \returns true iff successful - */ - bool read_aux_adc (int which_dboard, int which_adc, int *value); - - /*! - * \brief Read auxiliary analog to digital converter. - * - * \param which_dboard [0,1] which d'board - * \param which_adc [0,1] - * \returns value in the range [0,4095] if successful, else READ_FAILED. - */ - int read_aux_adc (int which_dboard, int which_adc); + int daughterboard_id (int which_side) const { return d_dbid[which_side & 0x1]; } + + bool set_pga (int which_amp, double gain_in_db); + double pga (int which_amp) const; + double pga_min () const; + double pga_max () const; + double pga_db_per_step () const; + + bool _write_oe (int which_side, int value, int mask); + bool write_io (int which_side, int value, int mask); + bool read_io (int which_side, int *value); + int read_io (int which_side); + bool write_refclk(int which_side, int value); + bool write_atr_mask(int which_side, int value); + bool write_atr_txval(int which_side, int value); + bool write_atr_rxval(int which_side, int value); + + bool write_aux_dac (int which_side, int which_dac, int value); + bool read_aux_adc (int which_side, int which_adc, int *value); + int read_aux_adc (int which_side, int which_adc); - /*! - * \brief returns current fusb block size - */ int block_size() const; - /*! - * \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. - * - * <pre> - * The 4 low bits are significant: - * - * ADC0 = (1 << 0) - * ADC1 = (1 << 1) - * ADC2 = (1 << 2) - * ADC3 = (1 << 3) - * </pre> - * - * By default the control loop is enabled on all ADC's. - */ - bool set_dc_offset_cl_enable(int bits, int mask); - // called in base class to derived class order bool start (); bool stop (); @@ -563,13 +873,13 @@ private: bool d_tx_enable; protected: - int d_dbid[2]; // Tx daughterboard ID's - /*! * \param which_board Which USRP board on usb (not particularly useful; use 0) * \param fusb_block_size fast usb xfer block size. Must be a multiple of 512. * Use zero for a reasonable default. * \param fusb_nblocks number of fast usb URBs to allocate. Use zero for a reasonable default. + * \param fpga_filename name of file that contains image to load into FPGA + * \param firmware_filename name of file that contains image to load into FX2 */ usrp_basic_tx (int which_board, int fusb_block_size=0, @@ -585,7 +895,6 @@ private: void restore_tx (bool on); // conditional set void probe_tx_slots (bool verbose); - int dboard_to_slot (int dboard) { return (dboard << 1) | 0; } public: @@ -598,14 +907,14 @@ public: * \param fusb_block_size fast usb xfer block size. Must be a multiple of 512. * Use zero for a reasonable default. * \param fusb_nblocks number of fast usb URBs to allocate. Use zero for a reasonable default. + * \param fpga_filename name of file that contains image to load into FPGA + * \param firmware_filename name of file that contains image to load into FX2 */ static usrp_basic_tx *make (int which_board, int fusb_block_size=0, int fusb_nblocks=0, const std::string fpga_filename = "", const std::string firmware_filename = "" ); - // MANIPULATORS - /*! * \brief tell the fpga the rate tx samples are going to the D/A's * @@ -634,138 +943,30 @@ public: */ void wait_for_completion (); - // ACCESSORS - //! sampling rate of D/A converter virtual long converter_rate() const { return fpga_master_clock_freq () * 2; } // 128M long dac_rate() const { return converter_rate(); } - long dac_freq() const { return converter_rate(); } //!< deprecated method name - - /*! - * \brief Return daughterboard ID for given Tx daughterboard slot [0,1]. - * - * \return daughterboard id >= 0 if successful - * \return -1 if no daugherboard - * \return -2 if invalid EEPROM on daughterboard - */ - int daughterboard_id (int which_dboard) const { return d_dbid[which_dboard & 0x1]; } - - // ---------------------------------------------------------------- - // routines for controlling the Programmable Gain Amplifier - /*! - * \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 { return -20.0; } + int daughterboard_id (int which_side) const { return d_dbid[which_side & 0x1]; } + + bool set_pga (int which_amp, double gain_in_db); + double pga (int which_amp) const; + double pga_min () const; + double pga_max () const; + double pga_db_per_step () const; + + bool _write_oe (int which_side, int value, int mask); + bool write_io (int which_side, int value, int mask); + bool read_io (int which_side, int *value); + int read_io (int which_side); + bool write_refclk(int which_side, int value); + bool write_atr_mask(int which_side, int value); + bool write_atr_txval(int which_side, int value); + bool write_atr_rxval(int which_side, int value); + + bool write_aux_dac (int which_side, int which_dac, int value); + bool read_aux_adc (int which_side, int which_adc, int *value); + int read_aux_adc (int which_side, int which_adc); - /*! - * \brief Return maximum legal PGA gain in dB. - */ - double pga_max () const { return 0.0; } - - /*! - * \brief Return hardware step size of PGA (linear in dB). - */ - double pga_db_per_step () const { return 20.0/255; } - - /*! - * \brief Write direction register (output enables) for pins that go to daughterboard. - * - * \param which_dboard [0,1] which d'board - * \param value value to write into register - * \param mask which bits of value to write into reg - * - * Each d'board has 16-bits of general purpose i/o. - * Setting the bit makes it an output from the FPGA to the d'board. - * - * This register is initialized based on a value stored in the - * d'board EEPROM. In general, you shouldn't be using this routine - * without a very good reason. Using this method incorrectly will - * kill your USRP motherboard and/or daughterboard. - */ - bool _write_oe (int which_dboard, int value, int mask); - - /*! - * \brief Write daughterboard i/o pin value - * - * \param which_dboard [0,1] which d'board - * \param value value to write into register - * \param mask which bits of value to write into reg - */ - bool write_io (int which_dboard, int value, int mask); - - /*! - * \brief Read daughterboard i/o pin value - * - * \param which_dboard [0,1] which d'board - * \param value return value - */ - bool read_io (int which_dboard, int *value); - - /*! - * \brief Read daughterboard i/o pin value - * - * \param which_dboard [0,1] which d'board - * \returns register value if successful, else READ_FAILED - */ - int read_io (int which_dboard); - - /*! - * \brief Write auxiliary digital to analog converter. - * - * \param which_dboard [0,1] which d'board - * N.B., SLOT_TX_A and SLOT_RX_A share the same AUX DAC's. - * SLOT_TX_B and SLOT_RX_B share the same AUX DAC's. - * \param which_dac [2,3] TX slots must use only 2 and 3. - * \param value [0,4095] - * \returns true iff successful - */ - bool write_aux_dac (int which_board, int which_dac, int value); - - /*! - * \brief Read auxiliary analog to digital converter. - * - * \param which_dboard [0,1] which d'board - * \param which_adc [0,1] - * \param value return 12-bit value [0,4095] - * \returns true iff successful - */ - bool read_aux_adc (int which_dboard, int which_adc, int *value); - - /*! - * \brief Read auxiliary analog to digital converter. - * - * \param which_dboard [0,1] which d'board - * \param which_adc [0,1] - * \returns value in the range [0,4095] if successful, else READ_FAILED. - */ - int read_aux_adc (int which_dboard, int which_adc); - - /*! - * \brief returns current fusb block size - */ int block_size() const; // called in base class to derived class order diff --git a/usrp/host/lib/legacy/usrp_standard.cc b/usrp/host/lib/legacy/usrp_standard.cc index 768a8f24e..d27bbbec3 100644 --- a/usrp/host/lib/legacy/usrp_standard.cc +++ b/usrp/host/lib/legacy/usrp_standard.cc @@ -1,6 +1,6 @@ /* -*- c++ -*- */ /* - * Copyright 2004 Free Software Foundation, Inc. + * Copyright 2004,2008 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -44,6 +44,168 @@ using namespace ad9862; #define NELEM(x) (sizeof (x) / sizeof (x[0])) +void +usrp_standard_common::calc_dxc_freq(double target_freq, double baseband_freq, double fs, + double *dxc_freq, bool *inverted) +{ + /* + Calculate the frequency to use for setting the digital up or down converter. + + @param target_freq: desired RF frequency (Hz) + @param baseband_freq: the RF frequency that corresponds to DC in the IF. + @param fs: converter sample rate + + @returns: 2-tuple (ddc_freq, inverted) where ddc_freq is the value + for the ddc and inverted is True if we're operating in an inverted + Nyquist zone. + */ + +#if 0 + printf("calc_dxc_freq:\n"); + printf(" target = %f\n", target_freq); + printf(" baseband = %f\n", baseband_freq); + printf(" fs = %f\n", fs); +#endif + + double delta = target_freq - baseband_freq; + + if(delta >= 0) { + while(delta > fs) { + delta -= fs; + } + if(delta <= fs/2) { // non-inverted region + *dxc_freq = -delta; + *inverted = false; + } + else { // inverted region + *dxc_freq = delta - fs; + *inverted = true; + } + } + else { + while(delta < -fs) { + delta += fs; + } + if(delta >= -fs/2) { + *dxc_freq = -delta; // non-inverted region + *inverted = false; + } + else { // inverted region + *dxc_freq = delta + fs; + *inverted = true; + } + } + +#if 0 + printf(" dxc_freq = %f\n", *dxc_freq); + printf(" inverted = %s\n", *inverted ? "true" : "false"); +#endif +} + + +/* + * Real lambda expressions would be _so_ much easier... + */ +class dxc_control { +public: + virtual bool is_tx() = 0; + virtual bool set_dxc_freq(double dxc_freq) = 0; + virtual double dxc_freq() = 0; +}; + +class ddc_control : public dxc_control { + usrp_standard_rx *d_u; + int d_chan; + +public: + ddc_control(usrp_standard_rx *u, int chan) + : d_u(u), d_chan(chan) {} + + bool is_tx(){ return false; } + bool set_dxc_freq(double dxc_freq){ return d_u->set_rx_freq(d_chan, dxc_freq); } + double dxc_freq(){ return d_u->rx_freq(d_chan); } +}; + +class duc_control : public dxc_control { + usrp_standard_tx *d_u; + int d_chan; + +public: + duc_control(usrp_standard_tx *u, int chan) + : d_u(u), d_chan(chan) {} + + bool is_tx(){ return true; } + bool set_dxc_freq(double dxc_freq){ return d_u->set_tx_freq(d_chan, dxc_freq); } + double dxc_freq() { return d_u->tx_freq(d_chan); } +}; + + +/*! + * \brief Tune such that target_frequency ends up at DC in the complex baseband + * + * \param db the daughterboard to use + * \param target_freq the center frequency we want at baseband (DC) + * \param fs the sample rate + * \param dxc DDC or DUC access and control object + * \param[out] result details of what we did + * + * \returns true iff operation was successful + * + * 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. + */ +static bool +tune_a_helper(db_base_sptr db, double target_freq, double fs, + dxc_control &dxc, usrp_tune_result *result) +{ + bool inverted = false; + double dxc_freq; + double actual_dxc_freq; + + // Ask the d'board to tune as closely as it can to target_freq +#if 0 + bool ok = db->set_freq(target_freq, &result->baseband_freq); +#else + bool ok; + { + freq_result_t fr = db->set_freq(target_freq); + ok = fr.ok; + result->baseband_freq = fr.baseband_freq; + } +#endif + + // Calculate the DDC setting that will downconvert the baseband from the + // daughterboard to our target frequency. + usrp_standard_common::calc_dxc_freq(target_freq, result->baseband_freq, fs, + &dxc_freq, &inverted); + + // If the spectrum is inverted, and the daughterboard doesn't do + // quadrature downconversion, we can fix the inversion by flipping the + // sign of the dxc_freq... (This only happens using the basic_rx board) + + if(db->spectrum_inverted()) + inverted = !inverted; + + if(inverted && !db->is_quadrature()){ + dxc_freq = -dxc_freq; + inverted = !inverted; + } + + if (dxc.is_tx()) // down conversion versus up conversion + dxc_freq = -dxc_freq; + + ok &= dxc.set_dxc_freq(dxc_freq); + actual_dxc_freq = dxc.dxc_freq(); + + result->dxc_freq = dxc_freq; + result->residual_freq = dxc_freq - actual_dxc_freq; + result->inverted = inverted; + return ok; +} + + static unsigned int compute_freq_control_word_fpga (double master_freq, double target_freq, double *actual_freq, bool verbose) @@ -210,7 +372,7 @@ usrp_standard_rx::stop () return ok; } -usrp_standard_rx * +usrp_standard_rx_sptr usrp_standard_rx::make (int which_board, unsigned int decim_rate, int nchan, int mux, int mode, @@ -219,21 +381,18 @@ usrp_standard_rx::make (int which_board, const std::string firmware_filename ) { - usrp_standard_rx *u = 0; - try { - u = new usrp_standard_rx (which_board, decim_rate, - nchan, mux, mode, - fusb_block_size, fusb_nblocks, - fpga_filename, firmware_filename); + usrp_standard_rx_sptr u = + usrp_standard_rx_sptr(new usrp_standard_rx(which_board, decim_rate, + nchan, mux, mode, + fusb_block_size, fusb_nblocks, + fpga_filename, firmware_filename)); + u->init_db(u); return u; } catch (...){ - delete u; - return 0; + return usrp_standard_rx_sptr(); } - - return u; } bool @@ -371,6 +530,112 @@ usrp_standard_rx::write_hw_mux_reg () return ok; } +int +usrp_standard_rx::determine_rx_mux_value(const usrp_subdev_spec &ss) +{ + /* + Determine appropriate Rx mux value as a function of the subdevice choosen and the + characteristics of the respective daughterboard. + + @param u: instance of USRP source + @param subdev_spec: return value from subdev option parser. + @type subdev_spec: (side, subdev), where side is 0 or 1 and subdev is 0 or 1 + @returns: the Rx mux value + + Figure out which A/D's to connect to the DDC. + + Each daughterboard consists of 1 or 2 subdevices. (At this time, + all but the Basic Rx have a single subdevice. The Basic Rx + has two independent channels, treated as separate subdevices). + subdevice 0 of a daughterboard may use 1 or 2 A/D's. We determine this + by checking the is_quadrature() method. If subdevice 0 uses only a single + A/D, it's possible that the daughterboard has a second subdevice, subdevice 1, + and it uses the second A/D. + + If the card uses only a single A/D, we wire a zero into the DDC Q input. + + (side, 0) says connect only the A/D's used by subdevice 0 to the DDC. + (side, 1) says connect only the A/D's used by subdevice 1 to the DDC. + */ + + struct truth_table_element + { + int d_side; + int d_uses; + bool d_swap_iq; + unsigned int d_mux_val; + + truth_table_element(int side, unsigned int uses, bool swap_iq, unsigned int mux_val=0) + : d_side(side), d_uses(uses), d_swap_iq(swap_iq), d_mux_val(mux_val){} + + bool operator==(const truth_table_element &in) + { + return (d_side == in.d_side && d_uses == in.d_uses && d_swap_iq == in.d_swap_iq); + } + + unsigned int mux_val() { return d_mux_val; } + }; + + + if (!is_valid(ss)) + throw std::invalid_argument("subdev_spec"); + + + // This is a tuple of length 1 or 2 containing the subdevice + // classes for the selected side. + std::vector<db_base_sptr> db = this->db(ss.side); + + unsigned int subdev0_uses, subdev1_uses, uses; + + // compute bitmasks of used A/D's + + if(db[0]->is_quadrature()) + subdev0_uses = 0x3; // uses A/D 0 and 1 + else + subdev0_uses = 0x1; // uses A/D 0 only + + if(db.size() > 1) // more than 1 subdevice? + subdev1_uses = 0x2; // uses A/D 1 only + else + subdev1_uses = 0x0; // uses no A/D (doesn't exist) + + if(ss.subdev == 0) + uses = subdev0_uses; + else if(ss.subdev == 1) + uses = subdev1_uses; + else + throw std::invalid_argument("subdev_spec"); + + if(uses == 0){ + throw std::runtime_error("Daughterboard doesn't have a subdevice 1"); + } + + bool swap_iq = db[0]->i_and_q_swapped(); + + truth_table_element truth_table[8] = { + // (side, uses, swap_iq) : mux_val + truth_table_element(0, 0x1, false, 0xf0f0f0f0), + truth_table_element(0, 0x2, false, 0xf0f0f0f1), + truth_table_element(0, 0x3, false, 0x00000010), + truth_table_element(0, 0x3, true, 0x00000001), + truth_table_element(1, 0x1, false, 0xf0f0f0f2), + truth_table_element(1, 0x2, false, 0xf0f0f0f3), + truth_table_element(1, 0x3, false, 0x00000032), + truth_table_element(1, 0x3, true, 0x00000023) + }; + size_t nelements = sizeof(truth_table)/sizeof(truth_table[0]); + + truth_table_element target(ss.side, uses, swap_iq, 0); + + size_t i; + for(i = 0; i < nelements; i++){ + if (truth_table[i] == target) + return truth_table[i].mux_val(); + } + throw std::runtime_error("internal error"); +} + + bool usrp_standard_rx::set_rx_freq (int channel, double freq) @@ -379,7 +644,7 @@ usrp_standard_rx::set_rx_freq (int channel, double freq) return false; unsigned int v = - compute_freq_control_word_fpga (adc_freq(), + compute_freq_control_word_fpga (adc_rate(), freq, &d_rx_freq[channel], d_verbose); @@ -493,6 +758,14 @@ usrp_standard_rx::format_bypass_halfband(unsigned int format) return (format & bmFR_RX_FORMAT_BYPASS_HB) != 0; } +bool +usrp_standard_rx::tune(int chan, db_base_sptr db, double target_freq, usrp_tune_result *result) +{ + ddc_control dxc(this, chan); + return tune_a_helper(db, target_freq, converter_rate(), dxc, result); +} + + ////////////////////////////////////////////////////////////////// @@ -589,7 +862,7 @@ usrp_standard_tx::stop () return ok; } -usrp_standard_tx * +usrp_standard_tx_sptr usrp_standard_tx::make (int which_board, unsigned int interp_rate, int nchan, int mux, @@ -598,20 +871,17 @@ usrp_standard_tx::make (int which_board, const std::string firmware_filename ) { - usrp_standard_tx *u = 0; - try { - u = new usrp_standard_tx (which_board, interp_rate, nchan, mux, - fusb_block_size, fusb_nblocks, - fpga_filename, firmware_filename); + usrp_standard_tx_sptr u = + usrp_standard_tx_sptr(new usrp_standard_tx(which_board, interp_rate, nchan, mux, + fusb_block_size, fusb_nblocks, + fpga_filename, firmware_filename)); + u->init_db(u); return u; } catch (...){ - delete u; - return 0; + return usrp_standard_tx_sptr(); } - - return u; } bool @@ -668,6 +938,39 @@ usrp_standard_tx::write_hw_mux_reg () return ok; } +int +usrp_standard_tx::determine_tx_mux_value(const usrp_subdev_spec &ss) +{ + /* + Determine appropriate Tx mux value as a function of the subdevice choosen. + + @param u: instance of USRP source + @param subdev_spec: return value from subdev option parser. + @type subdev_spec: (side, subdev), where side is 0 or 1 and subdev is 0 + @returns: the Rx mux value + + This is simpler than the rx case. Either you want to talk + to side A or side B. If you want to talk to both sides at once, + determine the value manually. + */ + + if (!is_valid(ss)) + throw std::invalid_argument("subdev_spec"); + + std::vector<db_base_sptr> db = this->db(ss.side); + + if(db[0]->i_and_q_swapped()) { + unsigned int mask[2] = {0x0089, 0x8900}; + return mask[ss.side]; + } + else { + unsigned int mask[2] = {0x0098, 0x9800}; + return mask[ss.side]; + } +} + + + #ifdef USE_FPGA_TX_CORDIC bool @@ -679,7 +982,7 @@ usrp_standard_tx::set_tx_freq (int channel, double freq) // This assumes we're running the 4x on-chip interpolator. unsigned int v = - compute_freq_control_word_fpga (dac_freq () / 4, + compute_freq_control_word_fpga (dac_rate () / 4, freq, &d_tx_freq[channel], d_verbose); @@ -700,17 +1003,17 @@ usrp_standard_tx::set_tx_freq (int channel, double freq) coarse_mod_t cm; double coarse; - assert (dac_freq () == 128000000); + assert (dac_rate () == 128000000); if (freq < -44e6) // too low return false; else if (freq < -24e6){ // [-44, -24) cm = CM_NEG_FDAC_OVER_4; - coarse = -dac_freq () / 4; + coarse = -dac_rate () / 4; } else if (freq < -8e6){ // [-24, -8) cm = CM_NEG_FDAC_OVER_8; - coarse = -dac_freq () / 8; + coarse = -dac_rate () / 8; } else if (freq < 8e6){ // [-8, 8) cm = CM_OFF; @@ -718,11 +1021,11 @@ usrp_standard_tx::set_tx_freq (int channel, double freq) } else if (freq < 24e6){ // [8, 24) cm = CM_POS_FDAC_OVER_8; - coarse = dac_freq () / 8; + coarse = dac_rate () / 8; } else if (freq <= 44e6){ // [24, 44] cm = CM_POS_FDAC_OVER_4; - coarse = dac_freq () / 4; + coarse = dac_rate () / 4; } else // too high return false; @@ -738,7 +1041,7 @@ usrp_standard_tx::set_tx_freq (int channel, double freq) // (This is required to use the fine modulator.) unsigned int v = - compute_freq_control_word_9862 (dac_freq () / 4, + compute_freq_control_word_9862 (dac_rate () / 4, fine, &d_tx_freq[channel], d_verbose); d_tx_freq[channel] += coarse; // adjust actual @@ -837,3 +1140,10 @@ usrp_standard_tx::coarse_modulator (int channel) const return d_coarse_mod[channel]; } + +bool +usrp_standard_tx::tune(int chan, db_base_sptr db, double target_freq, usrp_tune_result *result) +{ + duc_control dxc(this, chan); + return tune_a_helper(db, target_freq, converter_rate(), dxc, result); +} diff --git a/usrp/host/lib/legacy/usrp_standard.h b/usrp/host/lib/legacy/usrp_standard.h index 93a8aa56d..b7145b843 100644 --- a/usrp/host/lib/legacy/usrp_standard.h +++ b/usrp/host/lib/legacy/usrp_standard.h @@ -1,6 +1,6 @@ /* -*- c++ -*- */ /* - * Copyright 2004 Free Software Foundation, Inc. + * Copyright 2004,2008 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -24,10 +24,18 @@ #define INCLUDED_USRP_STANDARD_H #include <usrp_basic.h> +#include <boost/shared_ptr.hpp> +#include <usrp_tune_result.h> + +class usrp_standard_tx; +class usrp_standard_rx; + +typedef boost::shared_ptr<usrp_standard_tx> usrp_standard_tx_sptr; +typedef boost::shared_ptr<usrp_standard_rx> usrp_standard_rx_sptr; class usrp_standard_common { - int d_fpga_caps; // capability register val + int d_fpga_caps; // capability register val protected: usrp_standard_common(usrp_basic *parent); @@ -56,6 +64,18 @@ public: * This will be 0, 1, or 2. */ int nducs() const; + + /*! + * \brief Calculate the frequency to use for setting the digital up or down converter. + * + * \param target_freq is the desired RF frequency (Hz). + * \param baseband_freq is the RF frequency that corresponds to DC in the IF coming from the d'board. + * \param fs is the sampling frequency. + * \param[out] dxc_freq the frequency to program into the DDC (or DUC). + * \param[out] inverted is true if we're operating in an inverted Nyquist zone. + */ + static void calc_dxc_freq(double target_freq, double baseband_freq, double fs, + double *dxc_freq, bool *inverted); }; /*! @@ -63,7 +83,7 @@ public: * * Assumes digital down converter in FPGA */ -class usrp_standard_rx : public usrp_basic_rx, usrp_standard_common +class usrp_standard_rx : public usrp_basic_rx, public usrp_standard_common { private: static const int MAX_CHAN = 4; @@ -99,23 +119,23 @@ class usrp_standard_rx : public usrp_basic_rx, usrp_standard_common ~usrp_standard_rx (); /*! - * \brief invokes constructor, returns instance or 0 if trouble + * \brief invokes constructor, returns shared_ptr or shared_ptr equivalent of 0 if trouble * * \param which_board Which USRP board on usb (not particularly useful; use 0) * \param fusb_block_size fast usb xfer block size. Must be a multiple of 512. * Use zero for a reasonable default. * \param fusb_nblocks number of fast usb URBs to allocate. Use zero for a reasonable default. */ - static usrp_standard_rx *make (int which_board, - unsigned int decim_rate, - int nchan = 1, - int mux = -1, - int mode = 0, - int fusb_block_size = 0, - int fusb_nblocks = 0, - const std::string fpga_filename = "", - const std::string firmware_filename = "" - ); + static usrp_standard_rx_sptr make(int which_board, + unsigned int decim_rate, + int nchan = 1, + int mux = -1, + int mode = 0, + int fusb_block_size = 0, + int fusb_nblocks = 0, + const std::string fpga_filename = "", + const std::string firmware_filename = "" + ); /*! * \brief Set decimator rate. \p rate MUST BE EVEN and in [8, 256]. * @@ -155,6 +175,12 @@ class usrp_standard_rx : public usrp_basic_rx, usrp_standard_common bool set_mux (int mux); /*! + * Determine the appropriate Rx mux value as a function of the subdevice choosen + * and the characteristics of the respective daughterboard. + */ + int determine_rx_mux_value(const usrp_subdev_spec &ss); + + /*! * \brief set the frequency of the digital down converter. * * \p channel must be in the range [0,3]. \p freq is the center @@ -214,6 +240,22 @@ class usrp_standard_rx : public usrp_basic_rx, usrp_standard_common static bool format_want_q(unsigned int format); static bool format_bypass_halfband(unsigned int format); + /*! + * \brief High-level "tune" method. Works for the single channel case. + * + * This method adjusts both the daughterboard LO and the DDC so that + * target_freq ends up at DC in the complex baseband samples. + * + * \param chan which DDC channel we're controlling (almost always 0). + * \param db the daughterboard we're controlling. + * \param target_freq the RF frequency we want at DC in the complex baseband. + * \param[out] tune_result details how the hardware was configured. + * + * \returns true iff everything was successful. + */ + bool tune(int chan, db_base_sptr db, double target_freq, usrp_tune_result *result); + + // ACCESSORS unsigned int decim_rate () const; double rx_freq (int channel) const; @@ -233,7 +275,7 @@ class usrp_standard_rx : public usrp_basic_rx, usrp_standard_common * * Uses digital upconverter (coarse & fine modulators) in AD9862... */ -class usrp_standard_tx : public usrp_basic_tx, usrp_standard_common +class usrp_standard_tx : public usrp_basic_tx, public usrp_standard_common { public: enum coarse_mod_t { @@ -274,22 +316,22 @@ class usrp_standard_tx : public usrp_basic_tx, usrp_standard_common ~usrp_standard_tx (); /*! - * \brief invokes constructor, returns instance or 0 if trouble + * \brief invokes constructor, returns shared_ptr or shared_ptr equivalent of 0 if trouble * * \param which_board Which USRP board on usb (not particularly useful; use 0) * \param fusb_block_size fast usb xfer block size. Must be a multiple of 512. * Use zero for a reasonable default. * \param fusb_nblocks number of fast usb URBs to allocate. Use zero for a reasonable default. */ - static usrp_standard_tx *make (int which_board, - unsigned int interp_rate, - int nchan = 1, - int mux = -1, - int fusb_block_size = 0, - int fusb_nblocks = 0, - const std::string fpga_filename = "", - const std::string firmware_filename = "" - ); + static usrp_standard_tx_sptr make(int which_board, + unsigned int interp_rate, + int nchan = 1, + int mux = -1, + int fusb_block_size = 0, + int fusb_nblocks = 0, + const std::string fpga_filename = "", + const std::string firmware_filename = "" + ); /*! * \brief Set interpolator rate. \p rate must be in [4, 512] and a multiple of 4. @@ -343,6 +385,12 @@ class usrp_standard_tx : public usrp_basic_tx, usrp_standard_common bool set_mux (int mux); /*! + * Determine the appropriate Tx mux value as a function of the subdevice choosen + * and the characteristics of the respective daughterboard. + */ + int determine_tx_mux_value(const usrp_subdev_spec &ss); + + /*! * \brief set the frequency of the digital up converter. * * \p channel must be in the range [0,1]. \p freq is the center @@ -358,6 +406,22 @@ class usrp_standard_tx : public usrp_basic_tx, usrp_standard_common int nchannels () const; int mux () const; + /*! + * \brief High-level "tune" method. Works for the single channel case. + * + * This method adjusts both the daughterboard LO and the DUC so that + * DC in the complex baseband samples ends up at RF target_freq. + * + * \param chan which DUC channel we're controlling (usually == which_side). + * \param db the daughterboard we're controlling. + * \param target_freq the RF frequency we want our baseband translated to. + * \param[out] tune_result details how the hardware was configured. + * + * \returns true iff everything was successful. + */ + bool tune(int chan, db_base_sptr db, double target_freq, usrp_tune_result *result); + + // called in base class to derived class order bool start (); bool stop (); diff --git a/usrp/host/lib/legacy/usrp_subdev_spec.h b/usrp/host/lib/legacy/usrp_subdev_spec.h new file mode 100644 index 000000000..e841ff832 --- /dev/null +++ b/usrp/host/lib/legacy/usrp_subdev_spec.h @@ -0,0 +1,50 @@ +/* -*- 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 this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef INCLUDED_USRP_SUBDEV_SPEC_H +#define INCLUDED_USRP_SUBDEV_SPEC_H + +/*! + * \brief specify a daughterboard and subdevice on a daughterboard. + * + * In the USRP1, there are two sides, A and B. + * + * A daughterboard generally implements a single subdevice, but may in + * general implement any number of subdevices. At this time, all daughterboards + * with the exception of the Basic Rx and LF Rx implement a single subdevice. + * + * The Basic Rx and LF Rx implement 2 subdevices (soon 3). Subdevice + * 0 routes input RX-A to the DDC I input and routes a constant zero + * to the DDC Q input. Similarly, subdevice 1 routes input RX-B to + * the DDC I input and routes a constant zero to the DDC Q + * input. Subdevice 2 (when implemented) will route RX-A to the DDC I + * input and RX-B to the DDC Q input. + */ + +struct usrp_subdev_spec { + unsigned int side; // 0 -> A; 1 -> B + unsigned int subdev; // which subdevice (usually zero) + + usrp_subdev_spec(unsigned int _side = 0, unsigned int _subdev = 0) + : side(_side), subdev(_subdev) {} +}; + +#endif /* INCLUDED_USRP_SUBDEV_SPEC_H */ diff --git a/usrp/host/lib/legacy/usrp_tune_result.h b/usrp/host/lib/legacy/usrp_tune_result.h new file mode 100644 index 000000000..200541a37 --- /dev/null +++ b/usrp/host/lib/legacy/usrp_tune_result.h @@ -0,0 +1,44 @@ +/* -*- 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 this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef INCLUDED_USRP_TUNE_RESULT_H +#define INCLUDED_USRP_TUNE_RESULT_H + +class usrp_tune_result +{ +public: + // RF frequency that corresponds to DC in the IF + double baseband_freq; + + // frequency programmed into the DDC/DUC + double dxc_freq; + + // residual frequency (typically < 0.01 Hz) + double residual_freq; + + // is the spectrum inverted? + bool inverted; + + usrp_tune_result(double baseband=0, double dxc=0, double residual=0, bool _inverted=false) + : baseband_freq(baseband), dxc_freq(dxc), + residual_freq(residual), inverted(_inverted) {} +}; + +#endif /* INCLUDED_USRP_TUNE_RESULT_H */ |