//============================================================================
//
//  This file is part of GPSTk, the GPS Toolkit.
//
//  The GPSTk is free software; you can redistribute it and/or modify
//  it under the terms of the GNU Lesser General Public License as published
//  by the Free Software Foundation; either version 3.0 of the License, or
//  any later version.
//
//  The GPSTk 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 Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with GPSTk; if not, write to the Free Software Foundation,
//  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
//  
//  Copyright 2004, The University of Texas at Austin
//
//============================================================================

//============================================================================
//
//This software developed by Applied Research Laboratories at the University of
//Texas at Austin, under contract to an agency or agencies within the U.S. 
//Department of Defense. The U.S. Government retains all rights to use,
//duplicate, distribute, disclose, or release this software. 
//
//Pursuant to DoD Directive 523024 
//
// DISTRIBUTION STATEMENT A: This software has been approved for public 
//                           release, distribution is unlimited.
//
//=============================================================================

/** @file OrbitEph.hpp Encapsulates the "least common denominator"
 * orbit parameters that determine a satellite ephemeris, that is,
 * clock model, Kepler orbit elements plus harmonic perturbations with
 * time of ephemeris, satellite ID, and begin and end times of
 * validity.  Although it can also be used alone, this class is most
 * often to be used as a base class for a fuller implementation of the
 * ephemeris and clock, by adding health and accuracy information, fit
 * interval, ionospheric correction terms and data flags. It serves as
 * the base class for broadcast ephemerides for GPS, QZSS, Galileo,
 * and BeiDou, with RINEX Navigation input, among others. */

#ifndef GPSTK_ORBITEPH_HPP
#define GPSTK_ORBITEPH_HPP

#include <string>
#include "Exception.hpp"
#include "CommonTime.hpp"
#include "ObsID.hpp"
#include "SatID.hpp"
#include "Xvt.hpp"
//#include "Rinex3NavData.hpp"

namespace gpstk
{
      /** @defgroup GNSSEph GNSS Ephemeris Processing
       * Tools for processing and computing GNSS SV positions */

      /// @ingroup GNSSEph
      //@{

   class OrbitEph
   {
   public:
         /// Default constuctor
      OrbitEph(void) : dataLoadedFlag(false), dndot(0.0), Adot(0.0),
                       beginValid(CommonTime::END_OF_TIME),
                       endValid(CommonTime::BEGINNING_OF_TIME)
      { }

         /// Destructor
      virtual ~OrbitEph(void) {}

         /** Create a copy of this object and return a pointer to
          * it. This function MUST be overridden in any derived class
          * using the derived class name. */
      virtual OrbitEph* clone(void) const
      { return new OrbitEph(*this); }

         /** Returns true if the time, ct, is within the period of
          * validity of this OrbitEph object.
          * @throw Invalid Request if the required data has not been stored. */
      virtual bool isValid(const CommonTime& ct) const;

         /** Return true if orbit data have been loaded.
          * Returns false if no data have been loaded. */
      virtual bool dataLoaded(void) const
      { return dataLoadedFlag; }

         /// Return a string that will identify the derived class
      virtual std::string getName(void) const
      { return std::string("OrbitEph"); }

         /** This function returns the health status of the
          * SV. OrbitEph has no health information, so it returns
          * true; however the derived class should override this
          * function, computing a meaningful health. */
      virtual bool isHealthy(void) const
      {
         if(!dataLoadedFlag) GPSTK_THROW(InvalidRequest("Data not loaded"));
         return true;
      }

         /** Compute the satellite clock bias (seconds) at the given time
          * @throw Invalid Request if the required data has not been stored. */
      double svClockBias(const CommonTime& t) const;

         /** Compute the satellite clock drift (sec/sec) at the given time
          * @throw Invalid Request if the required data has not been stored. */
      double svClockDrift(const CommonTime& t) const;
      
         /** Compute satellite position at the given time.
          * This implements equations of motion as defined in IS-GPS-200.
          * (This code has its origins in 1980's FORTRAN code that has
          * been ported to C, then C++, then became part of the gpstk.
          * The original code was based on IS-GPS-200 Table 20-IV.
          * In July 2013, the code was modified to conform to Table 30-II
          * which includes additional time-dependent terms (A(dot) 
          * and delta n(dot)) that are in CNAV but not in LNAV.  These
          * changes should be backward compatible with LNAV as long as the 
          * Adot and dndot variables are appropriately set to 0.0 by the 
          * LNAV loaders.) 
          * @throw Invalid Request if the required data has not been stored. */
      Xvt svXvt(const CommonTime& t) const;

         /** Compute satellite relativity correction (sec) at the given time
          * @throw Invalid Request if the required data has not been stored. */
      double svRelativity(const CommonTime& t) const;

         /** adjustBeginningValidity determines the beginValid and
          * endValid times.  In OrbitEph it simply assumes a 4-hour
          * fit interval; however the derived class should override
          * this function, using an appropriate fit interval.
          * @throw Invalid Request if the required data has not been stored. */
      virtual void adjustValidity(void)
      {
         if(!dataLoadedFlag) GPSTK_THROW(InvalidRequest("Data not loaded"));
         beginValid = ctToe - 7200.0;
         endValid = ctToe + 7200.0;
      }
      
         /** Dump the overhead information as a string containing a
          * single line.
          * @throw Invalid Request if the required data has not been stored. */
      virtual std::string asString(void) const;

         /** Utility routine for dumpBody(); return the time in the
          * appropriate time system as a string.  Override for other
          * than GPS time systems.
          * @param t time to display
          * @param showHead if true, print only a header (default
          *   false) */
      virtual std::string timeDisplay(const CommonTime& t, bool showHead=false)
         const;

         /** Dump the overhead information to the given output stream.
          * @throw Invalid Request if the required data has not been stored. */
      virtual void dumpHeader(std::ostream& os = std::cout) const;

         /** Dump the orbit, etc information to the given output stream.
          * @throw Invalid Request if the required data has not been stored. */
      virtual void dumpBody(std::ostream& os = std::cout) const;

         /** Dump the object to the given output stream.
          * @throw Invalid Request if the required data has not been stored. */
      virtual void dump(std::ostream& os = std::cout) const
      {
         dumpHeader(os);
         dumpBody(os);
      }

         /** Define this OrbitEph by converting the given RINEX
          * navigation data.
          * @note this will be both overridden and called by the
          *   derived classes
          * @note currently has fixes for MGEX data.
          * @param rnd Rinex3NavData
          * @return true if OrbitEph was defined, false otherwise */
         //virtual bool load(const Rinex3NavData& rnd);

         // member data
     
         // overhead
      bool dataLoadedFlag; ///< True if data is present
      SatID satID;         ///< Define satellite system and specific SV
      ObsID obsID;         ///< Defines carrier and tracking code
      CommonTime ctToe;    ///< Ephemeris epoch

         // Clock information
      CommonTime ctToc;    ///< Clock Epoch
      double af0;          ///< SV clock error (sec)
      double af1;          ///< SV clock drift (sec/sec)
      double af2;          ///< SV clock drift rate (sec/sec**2)

         // Major orbit parameters
      double M0;           ///< Mean anomaly (rad)
      double dn;           ///< Correction to mean motion (rad/sec)
      double ecc;          ///< Eccentricity
      double A;            ///< Semi-major axis (m)
      double OMEGA0;       ///< Rt ascension of ascending node (rad)
      double i0;           ///< Inclination (rad)
      double w;            ///< Argument of perigee (rad)
      double OMEGAdot;     ///< Rate of Rt ascension (rad/sec)
      double idot;         ///< Rate of inclination angle (rad/sec)
         // Orbit parameters for modernized message
      double dndot;        ///< Rate of correction to mean motion (rad/sec/sec)
      double Adot;         ///< Rate of semi-major axis (m/sec)

         // Harmonic perturbations
      double Cuc;          ///< Cosine latitude (rad)
      double Cus;          ///< Sine latitude (rad)
      double Crc;          ///< Cosine radius (m)
      double Crs;          ///< Sine radius (m)
      double Cic;          ///< Cosine inclination (rad)
      double Cis;          ///< Sine inclination (rad)

      CommonTime beginValid;  ///< Time at beginning of validity
      CommonTime endValid;    ///< Time at end of fit validity

   }; // end class OrbitEph

      //@}
   
      /// Write OrbitEph to output stream
   std::ostream& operator<<(std::ostream& os, const OrbitEph& eph);

} // end namespace

#endif // GPSTK_ORBITEPH_HPP
