/* -*- c++ -*- */
/*
 * Copyright 2003 Free Software Foundation, Inc.
 * 
 * This file is part of GNU Radio
 * 
 * GNU Radio is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 * 
 * GNU Radio is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with GNU Radio; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

/*
 * ----------------------------------------------------------------
 * High level interface to the Universal Software Radio Peripheral
 * ----------------------------------------------------------------
 */

#ifndef INCLUDED_USRP0_H
#define INCLUDED_USRP0_H

struct usb_dev_handle;
class  fusb_devhandle;
class  fusb_ephandle;

/*!
 * \brief base class for usrp operations
 */
class usrp0_common {
private:
  // NOT IMPLEMENTED
  usrp0_common (const usrp0_common &rhs);			// no copy constructor
  usrp0_common &operator= (const usrp0_common &rhs);	// no assigment operator

protected:
  static const int	 MAX_CHAN = 4;
  struct usb_dev_handle	*d_udh;
  unsigned int		 d_ext_clk_div;

  usrp0_common (int which_board);	// throws if trouble
  ~usrp0_common ();

public:

  // MANIPULATORS

  /*!
   * \brief set clock divisor used to generate BNC clock signal.
   *
   * The master oscillator is divided by div [1,255] to give the external
   * clock signal.
   */
  bool set_ext_clk_div (unsigned int div);

  // ACCESSORS

  /*!  
   * \brief return frequency of master oscillator on USRP
   */
  double  get_oscillator_freq () const;

  unsigned int get_ext_clk_div () const;

  //
  // Low level implementation routines.  Subject to change.
  // You don't want to be using these unless your name is Matt or Eric ;-)
  //
  bool _write_fpga_reg (int regno, int value);	// 8-bit regno, 32-bit value
  bool _set_led (int which, bool on);
  bool _set_fpga_reset (bool on);
  bool _set_fpga_tx_enable (bool on);
  bool _set_fpga_rx_enable (bool on);
  bool _set_sleep_bits (int bits, int mask);
};

/*!
 * \brief class for accessing the receive side of the USRP
 */
class usrp0_rx : public usrp0_common {
private:
  unsigned int		 d_adc_clk_div;
  unsigned int		 d_decim_rate;
  unsigned int		 d_decim_regval;
  double		 d_rx_freq[MAX_CHAN];
  fusb_devhandle	*d_devhandle;
  fusb_ephandle		*d_ephandle;
  int			 d_bytes_per_poll;	// how often to poll for underruns
  int			 d_bytes_seen;		// how many bytes we've seen
  bool			 d_first_read;

  void setup_status_polling ();

public:
  usrp0_rx (int which_board);	// throws if trouble
  ~usrp0_rx ();

  /*!
   * \brief invokes constructor, returns instance or 0 if trouble
   */
  static usrp0_rx *make (int which_board);

  // MANIPULATORS

  /*!
   * \brief read data from the D/A's via the FPGA.
   * \p len must be a multiple of 512 bytes.
   *
   * \returns the number of bytes read, or -1 on error.
   *
   * If overrun is non-NULL it will be set true iff an RX overrun is detected.
   */
  int read (void *buf, int len, bool *overrun);

  /*!
   * \brief Set ADC clock divisor.  Must be in the range [2,6].
   *
   * Set the clock divisor used to generate the ADC clock from
   * the master oscillator.  \p div must be in the range [2,6].
   */
  bool set_adc_clk_div (unsigned int div);

  /*!
   * \brief Set decimator rate.  \p rate must be in the set [1,2,4,8,16,32,64,128,256]
   *
   * The final complex sample rate across the USB is
   *   get_oscillator_freq () / get_adc_clk_div () / get_decim_rate ()
   */
  bool set_decim_rate  (unsigned int rate);

  /*!
   * \brief set the frequency of the digital down converter.
   *
   * \p channel must be in the range [0,3].  \p freq is the center
   * frequency in Hz.  \p freq may be either negative or postive.
   * set_adc_clk_div must be called before calling this.  The
   * frequency specified is quantized.  Use get_rx_freq to retrieve
   * the actual value used.
   */
  bool set_rx_freq (int channel, double freq);  

  // ACCESSORS
  unsigned int get_adc_clk_div () const;
  unsigned int get_decim_rate () const;
  double get_rx_freq (int channel) const;
  int block_size() const;

  // debug routines
  bool _set_decim_reg (unsigned int regval);
};

/*!
 * \brief class for accessing the transmit side of the USRP
 */
class usrp0_tx : public usrp0_common {
private:
  unsigned int		 d_interp_rate;
  unsigned int		 d_interp_regval;
  double		 d_tx_freq[MAX_CHAN];
  fusb_devhandle	*d_devhandle;
  fusb_ephandle		*d_ephandle;
  int			 d_bytes_per_poll;	// how often to poll for overruns
  int			 d_bytes_seen;		// how many bytes we've seen
  bool			 d_first_write;

  void setup_status_polling ();

public:
  usrp0_tx (int which_board);	// throws if trouble
  ~usrp0_tx ();

  /*!
   * \brief invokes constructor, returns instance or 0 if trouble
   */
  static usrp0_tx *make (int which_board);

  // MANIPULATORS

  /*!
   * \brief Write data to the A/D's via the FPGA.
   *
   * \p len must be a multiple of 512 bytes.
   * \returns number of bytes written or -1 on error.
   *
   * if \p underrun is non-NULL, it will be set to true iff
   * a transmit underrun condition is detected.
   */
  int write (const void *buf, int len, bool *underrun);

  /*!
   * \brief Set interpolator rate.  \p rate must be in the set [1,2,4,8,16,32,64,128,256]
   *
   * The final complex sample rate across the USB is
   *   get_oscillator_freq () / get_interp_rate ()
   */
  bool set_interp_rate (unsigned int rate);

  /*!
   * \brief set the frequency of the digital up converter.
   *
   * \p channel must be in the range [0,3].  \p freq is the center
   * frequency in Hz.  \p freq may be either negative or postive.
   * set_adc_clk_div must be called before calling this.  The
   * frequency specified is quantized.  Use get_tx_freq to retrieve
   * the actual value used.
   */
  bool set_tx_freq (int channel, double freq);  // chan: [0,3]

  /*
   * block until all outstanding writes have completed
   */
  void wait_for_completion ();

  // ACCESSORS
  unsigned int get_interp_rate () const;
  double get_tx_freq (int channel) const;
  int block_size() const;

  // debug routines
  bool _set_interp_reg (unsigned int regval);
};

#endif
