//============================================================================
//
//  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
//  Dagoberto Salazar - gAGE ( http://www.gage.es ). 2009
//
//============================================================================

//============================================================================
//
//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 Antenna.hpp
 * Encapsulates the data related to GNSS antennas.
 */

#ifndef GPSTK_ANTENNA_HPP
#define GPSTK_ANTENNA_HPP

#include <cmath>
#include <vector>
#include <map>
#include <string>

#include "Exception.hpp"
#include "CommonTime.hpp"
#include "Triple.hpp"



namespace gpstk
{

      /// @ingroup DataStructures 
      //@{

      /** This class encapsulates the data related to GNSS antennas according
       *  to IGS standards.
       *
       * Further information about antennas may be found in IGS ftp site:
       *
       *    ftp://igscb.jpl.nasa.gov/pub/station/general/
       *
       * At that site you'll find some very important files:
       *
       * \li rcvr_ant.tab: Official IGS naming conventions for GNSS equipment.
       * \li antex13.txt: ANTEX format definition.
       * \li igs05_wwww.atx: Absolute IGS phase center corrections for
       * satellite and receiver antennas. Field 'wwww' represents GPS week of
       * last file change.
       * \li igs05.atx: Link to latest igs05_wwww.atx file.
       * \li igs_01.atx: Relative IGS phase center corrections for
       * satellite and receiver antennas.
       *
       * This class is normally used in combination with 'AntexReader' class. A
       * typical way to use these classes follows:
       *
       * @code
       *      // Declare some Antenna objects
       *   Antenna antenna1;
       *   Antenna antenna2;
       *   Antenna satGPS02;
       *
       *      // Create AntexReader object
       *   AntexReader antexread;
       *
       *      // Open Antex file. 'igs05.atx' is for absolute phase centers,
       *      // while 'igs_01.atx' is for relative phase centers.
       *   antexread.open("igs05.atx");
       *
       *   double elevation( 34.5 );
       *   double azimuth( 163.2 );
       *
       *      // Get antenna data and accentricity for L1 for satellite GPS-07
       *      // at a specific epoch
       *   CivilTime epoch(2008, 6, 15, 10, 21, 12654.0);
       *   satGPS07 = antexread.getAntenna( "G07", epoch );
       *   cout << satGPS07.getAntennaPCOffset( Antenna::G01 ) << endl;
       *
       *      // Get antenna data and non-azimuth dependent phase center
       *      // offset value + eccentricity for L2.
       *      // Radome type is NOT taken into account
       *   antenna1  = antexread.getAntennaNoRadome("AOAD/M_B");
       *   cout << antenna1.getAntennaPCOffset( Antenna::G02, elevation );
       *
       *      // Get antenna data and azimuth-dependent phase center offset
       *      // value + eccentricity for L1.
       *      // Ask for a specific "strict IGS" model+radome combination
       *   std::string strictIGSModel( "ASH700936B_M    SNOW" );
       *   antenna2  = antexread.getAntenna( strictIGSModel );
       *   cout << antenna2.getAntennaPCOffset( Antenna::G01,
       *                                        elevation,
       *                                        azimuth );
       *
       * @endcode
       *
       * @sa AntexReader.hpp
       */
   class Antenna
   {
   public:

         /// Frequency type
      enum frequencyType
      {
         G01,                    ///< L1 - GPS
         G02,                    ///< L2 - GPS
         G05,                    ///< L5 - GPS
         R01,                    ///< G1 - GLONASS
         R02,                    ///< G2 - GLONASS
         E01,                    ///< E1 - Galileo
         E05,                    ///< E5a - Galileo
         E07,                    ///< E5b - Galileo
         E08,                    ///< E5 (E5a+E5b) - Galileo
         E06                     ///< E6 - Galileo
      };


         /// Antenna data type
      enum AntennaDataType
      {
         antennaType,         ///< Antenna type. IGS standard 'rcvr_ant.tab'
         antennaRadome,       ///< Antenna radome. IGS standard 'rcvr_ant.tab'
         serial,              ///< Serial number/satellite code "CNN"
         cnnn,                ///< Satellite code "CNNN"
         cosparID,            ///< COSPAR ID "YYYY-XXXA"
         calMethod,           ///< Calibration method
         agency,              ///< Name of agency
         numAntennas,         ///< Number of calibrated antennas
         date,                ///< Date
         sinexCode            ///< SINEX code
      };


         // Handy type definitions

         /// Map containing antenna data.
      typedef std::map< AntennaDataType, std::string > AntennaDataMap;

         /// Map for antenna phase center eccentricities.
      typedef std::map< frequencyType, Triple > AntennaEccDataMap;

         /// Map for non-azimuth dependent phase center patterns.
      typedef std::map< frequencyType, std::vector<double> > NoAziDataMap;

         /// Map for azimuth dependent phase center patterns.
      typedef std::map< double, std::vector<double> > AzimuthDataMap;

         /// Map for azimuth dependent phase center patterns and frequencies.
      typedef std::map< frequencyType, AzimuthDataMap > PCDataMap;



         /// Default constructor.
      Antenna() {};


         /** Common constructor.
          *
          * @param[in] eccL1      Eccentricity Triple (meters) for GPS L1 freq.
          * @param[in] eccL2      Eccentricity Triple (meters) for GPS L2 freq.
          */
      Antenna( const Triple& eccL1,
               const Triple& eccL2 );


         /** Common constructor.
          *
          * @param[in] NorthEccL1   North eccentricity (meters) for GPS L1 freq
          * @param[in] EastEccL1    East eccentricity (meters) for GPS L1 freq
          * @param[in] UpEccL1      Up eccentricity (meters) for GPS L1 freq
          * @param[in] NorthEccL2   North eccentricity (meters) for GPS L2 freq
          * @param[in] EastEccL2    East eccentricity (meters) for GPS L2 freq
          * @param[in] UpEccL2      Up eccentricity (meters) for GPS L2 freq
          */
      Antenna( double NorthEccL1,
               double EastEccL1,
               double UpEccL1,
               double NorthEccL2,
               double EastEccL2,
               double UpEccL2 );


         /** Get antenna eccentricity (or 'phase center offset' in Antex
          *  parlance) as a Triple in UEN system.
          *
          * @param[in] freq      Frequency
          *
          * @warning The phase center offset Triple is in UEN system.
          */
      Triple getAntennaEccentricity( frequencyType freq ) const
         throw(InvalidRequest);


         /** Get antenna phase center variation. Use this method when you
          *  don't have azimuth dependent phase center patterns.
          *
          * This method returns a Triple, in UEN system, with the
          * elevation-dependent phase center variation.
          *
          * @param[in] freq      Frequency
          * @param[in] elevation Elevation (degrees)
          *
          * @warning The phase center variation Triple is in UEN system.
          */
      Triple getAntennaPCVariation( frequencyType freq,
                                    double elevation ) const
         throw(InvalidRequest);


         /** Get antenna phase center variation.
          *
          * This method returns a Triple, in UEN system, with the
          * elevation and azimuth-dependent phase center variation.
          *
          * @param[in] freq      Frequency
          * @param[in] elevation Elevation (degrees)
          * @param[in] azimuth   Azimuth (degrees)
          *
          * @warning The phase center variation Triple is in UEN system.
          */
      Triple getAntennaPCVariation( frequencyType freq,
                                    double elevation,
                                    double azimuth ) const
         throw(InvalidRequest);


         /** Get antenna data.
          *
          * @param[in] dataType     Antenna data type to be fetched
          */
      std::string getAntennaData( AntennaDataType dataType ) const
         throw(InvalidRequest);


         /** Set antenna data.
          *
          * @param[in] dataType     Antenna data type to be set
          * @param[in] data         String of data to be stored
          */
      Antenna setAntennaData( AntennaDataType dataType,
                              const std::string& data )
      { antennaData[dataType] = data; return (*this); };


         /// Get antenna type.
      std::string getAntennaType() const
         throw(InvalidRequest)
      { return getAntennaData( antennaType ); };


         /** Set antenna type.
          *
          * @param[in] type      Type of antenna. IGS standard 'rcvr_ant.tab'
          */
      Antenna setAntennaType( const std::string& type )
      { return setAntennaData( antennaType, type ); };


         /// Get antenna radome.
      std::string getAntennaRadome() const
         throw(InvalidRequest)
      { return getAntennaData( antennaRadome ); };


         /** Set antenna radome.
          *
          * @param[in] radome    Type of radome. IGS standard 'rcvr_ant.tab'
          */
      Antenna setAntennaRadome( const std::string& radome )
      { return setAntennaData( antennaRadome, radome ); };


         /// Get antenna serial number or satellite code "CNN".
      std::string getAntennaSerial() const
         throw(InvalidRequest)
      { return getAntennaData(serial); };


         /** Set antenna serial number or satellite code "CNN".
          *
          * @param[in] sn      Serial number/satellite code "CNN"
          */
      Antenna setAntennaSerial( const std::string& sn )
      { return setAntennaData( serial, sn ); };


         /// Get antenna calibration method.
      std::string getAntennaCalMethod() const
         throw(InvalidRequest)
      { return getAntennaData(calMethod); };


         /** Set antenna calibration method.
          *
          * @param[in] method       Antenna calibration method
          */
      Antenna setAntennaCalMethod( const std::string& method )
      { return setAntennaData( calMethod, method ); };


         /// Get start of antenna validity period.
      CommonTime getAntennaValidFrom() const
      { return validFrom; };


         /** Set start of antenna validity period.
          *
          * @param[in] valFrom       Start of validity period
          */
      Antenna setAntennaValidFrom( const CommonTime& valFrom )
      { validFrom = valFrom; return (*this); };


         /// Get end of antenna validity period.
      CommonTime getAntennaValidUntil() const
      { return validUntil; };


         /** Set end of antenna validity period.
          *
          * @param[in] valUntil      End of validity period
          */
      Antenna setAntennaValidUntil( const CommonTime& valUntil )
      { validUntil = valUntil; return (*this); };


         /// Get increment of the azimuth.
      double getDazi() const
      { return dazi; };


         /** Set increment of the azimuth.
          *
          * @param[in] daz      Increment of the azimuth
          */
      Antenna setDazi( double daz )
      { dazi = daz; return (*this); };


         /// Get initial zenith grid value.
      double getZen1() const
      { return zen1; };


         /** Set initial zenith grid value.
          *
          * @param[in] z1      Initial zenith grid value
          */
      Antenna setZen1( double z1 )
      { zen1 = z1; return (*this); };


         /// Get final zenith grid value.
      double getZen2() const
      { return zen2; };


         /** Set final zenith grid value.
          *
          * @param[in] z2      Final zenith grid value
          */
      Antenna setZen2( double z2 )
      { zen2 = z2; return (*this); };


         /// Get increment of the zenith.
      double getDzen() const
      { return dzen; };


         /** Set increment of the zenith.
          *
          * @param[in] dz      Increment of the zenith
          */
      Antenna setDzen( double dz )
      { dzen = dz; return (*this); };


         /// Get number of frequencies.
      int getNumFreq() const
      { return numFreq; };


         /** Set number of frequencies.
          *
          * @param[in] nFreq      Number of frequencies
          */
      Antenna setNumFreq( int nFreq )
      { numFreq = nFreq; return (*this); };


         /// Get antenna data map.
      AntennaDataMap getAntennaDataMap() const
      { return antennaData; };


         /** Set antenna data map.
          *
          * @param[in] dataMap       Antenna data map
          */
      Antenna setAntennaDataMap( const AntennaDataMap& dataMap )
      { antennaData = dataMap; return (*this); };


         /// Get antenna comments.
      std::vector<std::string> getAntennaComments() const
      { return commentList; };


         /** Set antenna comments.
          *
          * @param[in] comments       Antenna comments vector
          */
      Antenna setAntennaComments( const std::vector<std::string>& comments )
      { commentList = comments; return (*this); };


         /** Add antenna comments.
          *
          * @param[in] comments       Antenna comments line
          */
      Antenna addAntennaComments( std::string comments )
      { commentList.push_back(comments); return (*this); };


         /// Get antenna phase center eccentricities map, in METERS.
      AntennaEccDataMap getAntennaEccMap() const
      { return antennaEccMap; };


         /** Set antenna phase center eccentricities map, in METERS.
          *
          * @param[in] eccMap  Antenna phase center eccentricities map, METERS.
          */
      Antenna setAntennaEccMap( const AntennaEccDataMap& eccMap )
      { antennaEccMap = eccMap; return (*this); };


         /** Add antenna phase center ecccentricities, in METERS.
          *
          * @param[in] freq        Frequency.
          * @param[in] trEcc       Eccentricity Triple, in METERS.
          */
      Antenna addAntennaEcc( frequencyType freq,
                             const Triple& trEcc )
      { antennaEccMap[freq] = trEcc; return (*this); };


         /** Add antenna phase center eccentricities, in METERS.
          *
          * @param[in] freq        Frequency.
          * @param[in] northEcc    North eccentricity component, in METERS.
          * @param[in] eastEcc     East eccentricity component, in METERS.
          * @param[in] upEcc       Up eccentricity component, in METERS.
          */
      Antenna addAntennaEcc( frequencyType freq,
                              double northEcc,
                              double eastEcc,
                              double upEcc );


         /// Get antenna phase center RMS eccentricities map, in METERS.
      AntennaEccDataMap getAntennaRMSEccMap() const
      { return antennaRMSEccMap; };


         /** Set antenna phase center RMS eccentricities map, in METERS.
          *
          * @param[in] eccRMSMap    Antenna phase center RMS eccentricities
          *                         map, in METERS
          */
      Antenna setAntennaRMSEccMap(const AntennaEccDataMap& eccRMSMap)
      { antennaRMSEccMap = eccRMSMap; return (*this); };


         /** Add antenna phase center RMS eccentricities, in METERS.
          *
          * @param[in] freq        Frequency.
          * @param[in] northRMS    North eccentricity RMS component, in METERS.
          * @param[in] eastRMS     East eccentricity RMS component, in METERS.
          * @param[in] upRMS       Up eccentricity RMS component, in METERS.
          */
      Antenna addAntennaRMSEcc( frequencyType freq,
                                double northRMS,
                                double eastRMS,
                                double upRMS );


         /// Get antenna non-azimuth dependent patterns map, in METERS.
      NoAziDataMap getAntennaNoAziMap() const
      { return noAziMap; };


         /** Set antenna non-azimuth dependent patterns map, in METERS.
          *
          * @param[in] naMap Antenna non-azimuth dependent patterns map, METERS.
          */
      Antenna setAntennaNoAziMap( const NoAziDataMap& naMap )
      { noAziMap = naMap; return (*this); };


         /** Add antenna non-azimuth dependent pattern, in METERS.
          *
          * @param[in] freq        Frequency.
          * @param[in] pcVec       Vector of phase centers, in METERS.
          */
      Antenna addAntennaNoAziPattern( frequencyType freq,
                                      const std::vector<double>& pcVec )
      { noAziMap[freq] = pcVec; return (*this); };


         /// Get antenna azimuth dependent patterns map, in METERS.
      PCDataMap getAntennaPCMap() const
      { return pcMap; };


         /** Set antenna azimuth dependent patterns map, in METERS.
          *
          * @param[in] pMap Antenna azimuth dependent patterns map, METERS.
          */
      Antenna setAntennaPCMap( const PCDataMap& pMap )
      { pcMap = pMap; return (*this); };


         /** Add antenna azimuth dependent pattern, in METERS.
          *
          * @param[in] freq        Frequency.
          * @param[in] azi         Azimuth.
          * @param[in] pcVec       Vector of phase centers, in METERS.
          */
      Antenna addAntennaPattern( frequencyType freq,
                                 double azi,
                                 const std::vector<double>& pcVec )
      { pcMap[freq][azi] = pcVec; return (*this); };


         /// Get antenna non-azimuth dependent RMS map, in METERS.
      NoAziDataMap getAntennaNoAziRMSMap() const
      { return noAziRMSMap; };


         /** Set antenna non-azimuth dependent RMS map, in METERS.
          *
          * @param[in] naRMSMap Antenna non-azimuth dependent RMS map, METERS.
          */
      Antenna setAntennaNoAziRMSMap( const NoAziDataMap& naRMSMap )
      { noAziRMSMap = naRMSMap; return (*this); };


         /** Add antenna non-azimuth dependent RMS, in METERS.
          *
          * @param[in] freq        Frequency.
          * @param[in] pcRMS       Vector of phase centers RMS, in METERS.
          */
      Antenna addAntennaNoAziRMS( frequencyType freq,
                                  const std::vector<double>& pcRMS )
      { noAziRMSMap[freq] = pcRMS; return (*this); };


         /// Get antenna azimuth dependent RMS map, in METERS.
      PCDataMap getAntennaPCRMSMap() const
      { return pcRMSMap; };


         /** Set antenna azimuth dependent RMS map, in METERS.
          *
          * @param[in] pRMSMap Antenna azimuth dependent patterns map, METERS.
          */
      Antenna setAntennaPCRMSMap( const PCDataMap& pRMSMap )
      { pcRMSMap = pRMSMap; return (*this); };


         /** Add antenna azimuth dependent RMS, in METERS.
          *
          * @param[in] freq        Frequency.
          * @param[in] azi         Azimuth.
          * @param[in] pcRMSVec    Vector of phase centers RMS, in METERS.
          */
      Antenna addAntennaPatternRMS( frequencyType freq,
                                    double azi,
                                    const std::vector<double>& pcRMSVec )
      { pcRMSMap[freq][azi] = pcRMSVec; return (*this); };


         /// Get size of antenna data map.
      int getAntennaDataSize() const
      { return antennaData.size(); };


         /// Get size of antenna phase center eccentricities map.
      int getAntennaEccMapSize() const
      { return antennaEccMap.size(); };


         /// Get size of non-azimuth dependent phase center patterns map.
      int getNoAziMapSize() const
      { return noAziMap.size(); };


         /// Get size of azimuth dependent phase center patterns map.
      int getPCMapSize() const
      { return pcMap.size(); };


         /// Returns if this object is valid. The validity criteria is to
         /// have a non-empty 'antennaData' map AND a non-empty 'antennaEccMap'.
      bool isValid() const;


         /// Destructor
      virtual ~Antenna() {};


   private:


         /// Map with antenna data
      AntennaDataMap antennaData;


      double dazi;                     ///< Increment of the azimuth

      double zen1;                     ///< Initial zenith grid value
      double zen2;                     ///< Final zenith grid value
      double dzen;                     ///< Increment of the zenith

      int numFreq;                     ///< Number of frequencies

      CommonTime validFrom;               ///< Start of validity period
      CommonTime validUntil;              ///< End of validity period

      std::vector<std::string> commentList;      ///< Comments


         /// Map holding antenna phase center eccentricities, in METERS
      AntennaEccDataMap antennaEccMap;


         /// Non-azimuth dependent phase center patterns map
      NoAziDataMap noAziMap;


         /// Azimuth dependent phase center patterns map
      PCDataMap pcMap;


         /// Map holding antenna phase center RMS eccentricities, in METERS
      AntennaEccDataMap antennaRMSEccMap;


         /// Non-azimuth dependent RMS
      NoAziDataMap noAziRMSMap;


         /// Azimuth dependent phase center RMS
      PCDataMap pcRMSMap;


         /** Linear interpolation as function of normalized angle
          *
          * @param[in] dataVector         std::vector holding data.
          * @param[in] normalizedAngle    Normalized angle.
          *
          * 'normalizedAngle' is a value corresponding to the original angle
          * divided by the angle interval.
          */
      double linearInterpol( const std::vector<double>& dataVector,
                             double normalizedAngle ) const;


   }; // End of class 'Antenna'


      //@}

}  // End of namespace gpstk

#endif   // GPSTK_ANTENNA_HPP
