/**
 * @file ModeledReferencePR.hpp
 * Class to compute modeled pseudoranges using a reference station
 */

#ifndef GPSTK_MODELEDREFERENCEPR_HPP
#define GPSTK_MODELEDREFERENCEPR_HPP

//============================================================================
//
//  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 2.1 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//  
//  Dagoberto Salazar - gAGE ( http://www.gage.es ). 2006, 2007
//
//============================================================================



#include "ModeledPseudorangeBase.hpp"
#include "DayTime.hpp"
#include "EngEphemeris.hpp"
#include "EphemerisStore.hpp"
#include "EphemerisRange.hpp"
#include "TropModel.hpp"
#include "IonoModel.hpp"
#include "IonoModelStore.hpp"
#include "Geodetic.hpp"
#include "Position.hpp"
#include "icd_200_constants.hpp"
#include "TypeID.hpp"
#include "DataStructures.hpp"


namespace gpstk
{
      /** @addtogroup GPSsolutions */
      //@{

      /** This class compute modeled pseudoranges from satellites to a reference station.
       *
       * This class may be used either in a Vector- and Matrix-oriented way, or
       * with GNSS data structure objects from "DataStructures" class. In any
       * case, it is intented to be used with stations where the position is
       * known (there comes the name: Modeled Reference station PseudoRange).
       *
       * A typical way to use this class with GNSS data structures follows:
       *
       * @code
       *   RinexObsStream rin("ebre0300.02o");  // Data stream
       *   RinexNavStream rnavin("brdc0300.02n");   // Ephemeris data stream
       *   RinexNavData rNavData;
       *   BCEphemerisStore bceStore;
       *   while (rnavin >> rNavData) bceStore.addEphemeris(rNavData);
       *   bceStore.SearchPast();  // This is the default
       *
       *   RinexNavHeader rNavHeader;
       *   IonoModelStore ionoStore;
       *   IonoModel ioModel;
       *   rnavin >> rNavHeader;    // Read navigation RINEX header
       *   ioModel.setModel(rNavHeader.ionAlpha, rNavHeader.ionBeta);
       *   ionoStore.addIonoModel(DayTime::BEGINNING_OF_TIME, ioModel);
       *
       *   // EBRE station nominal position
       *   Position nominalPos(4833520.3800, 41536.8300, 4147461.2800);
       *
       *   // Declare a tropospheric model object, setting the defaults
       *   MOPSTropModel mopsTM(nominalPos.getAltitude(), nominalPos.getGeodeticLatitude(), 30);
       *
       *   // Declare the modeler object, setting all the parameters in one pass
       *   // As stated, it will compute the model using the C1 observable
       *   ModeledReferencePR modelRef(nominalPos, ionoStore, mopsTM, bceStore, TypeID::C1);
       *
       *   gnssRinex gRin;
       *
       *   while(rin >> gRin) {
       *      gRin >> modelRef;
       *   }
       * @endcode
       *
       * The "ModeledReferencePR" object will visit every satellite in the 
       * GNSS data structure that is "gRin" and will try to compute its 
       * model: Prefit residual, geometric distance, relativity delay,
       * ionospheric/tropospheric corrections, geometry matrix, etc.
       *
       * When used with the ">>" operator, this class returns the same incoming
       * data structure with the extra data inserted along their corresponding
       * satellites. Be warned that if a given satellite does not 
       * have the observations required, it will be summarily deleted from the data
       * structure.
       *
       * @sa ModeledPseudorangeBase.hpp for base class.
       *
       */
    class ModeledReferencePR : public ModeledPseudorangeBase
    {
    public:

        /// Implicit constructor
        ModeledReferencePR() throw(Exception) : useTGD(true), pDefaultIonoModel(NULL), pDefaultTropoModel(NULL), defaultObservable(TypeID::C1), pDefaultEphemeris(NULL)
        { 
            InitializeValues();
        };


        /** Explicit constructor taking as input reference station coordinates.
         *
         * Those coordinates may be Cartesian (X, Y, Z in meters) or Geodetic
         * (Latitude, Longitude, Altitude), but defaults to Cartesian. 
         *
         * Also, a pointer to GeoidModel may be specified, but default is NULL 
         * (in which case WGS84 values will be used).
         *
         * @param aRx   first coordinate [ X(m), or latitude (degrees N) ]
         * @param bRx   second coordinate [ Y(m), or longitude (degrees E) ]
         * @param cRx   third coordinate [ Z, height above ellipsoid or radius, in m ]
         * @param s     coordinate system (default is Cartesian, may be set to Geodetic).
         * @param geoid pointer to GeoidModel (default is null, implies WGS84)
         */
        ModeledReferencePR(const double& aRx, const double& bRx, const double& cRx, 
            Position::CoordinateSystem s = Position::Cartesian,
            GeoidModel *geoid = NULL) throw(Exception) : useTGD(true), pDefaultIonoModel(NULL), pDefaultTropoModel(NULL), defaultObservable(TypeID::C1), pDefaultEphemeris(NULL)
        { 
            InitializeValues();
            setInitialRxPosition(aRx, bRx, cRx, s, geoid);
        };


        /// Explicit constructor, taking as input a Position object containing reference station coordinates.
        ModeledReferencePR(const Position& RxCoordinates) throw(Exception) : useTGD(true), pDefaultIonoModel(NULL), pDefaultTropoModel(NULL), defaultObservable(TypeID::C1), pDefaultEphemeris(NULL)
        { 
            InitializeValues();
            setInitialRxPosition(RxCoordinates);
        };


        /** Explicit constructor, taking as input reference station coordinates, default
         * ionospheric and tropospheric models, ephemeris to be used, default observable 
         * and whether TGD will be computed or not.
         *
         * This constructor is meant to be used when working with GNSS data structures in
         * order to set the basic parameters from the beginning.
         *
         * @param RxCoordinates Reference station coordinates.
         * @param dIonoModel    Ionospheric model to be used by default.
         * @param dTropoModel   Tropospheric model to be used by default.
         * @param dEphemeris    EphemerisStore object to be used by default.
         * @param dObservable   Observable type to be used by default.
         * @param usetgd        Whether TGD will be used by default or not.
         *
         * @sa DataStructures.hpp.
         */
        ModeledReferencePR(const Position& RxCoordinates, IonoModelStore& dIonoModel, TropModel& dTropoModel, EphemerisStore& dEphemeris, const TypeID& dObservable, bool usetgd = true) throw(Exception) { 
            InitializeValues();
            setInitialRxPosition(RxCoordinates);
            setDefaultIonoModel(dIonoModel);
            setDefaultTropoModel(dTropoModel);
            setDefaultObservable(dObservable);
            setDefaultEphemeris(dEphemeris);
            useTGD = usetgd;
        };


        /** Explicit constructor, taking as input reference station coordinates, default
         * ionospheric model, ephemeris to be used, default observable and whether TGD 
         * will be computed or not.
         *
         * The default tropospheric model will be set to NULL.
         *
         * This constructor is meant to be used when working with GNSS data structures in
         * order to set the basic parameters from the beginning.
         *
         * @param RxCoordinates Reference station coordinates.
         * @param dIonoModel    Ionospheric model to be used by default.
         * @param dEphemeris    EphemerisStore object to be used by default.
         * @param dObservable   Observable type to be used by default.
         * @param usetgd        Whether TGD will be used by default or not.
         *
         * @sa DataStructures.hpp.
         */
        ModeledReferencePR(const Position& RxCoordinates, IonoModelStore& dIonoModel, EphemerisStore& dEphemeris, const TypeID& dObservable, bool usetgd = true) throw(Exception) : pDefaultTropoModel(NULL) { 
            InitializeValues();
            setInitialRxPosition(RxCoordinates);
            setDefaultIonoModel(dIonoModel);
            setDefaultObservable(dObservable);
            setDefaultEphemeris(dEphemeris);
            useTGD = usetgd;
        };


        /** Explicit constructor, taking as input reference station coordinates, default
         * tropospheric model, ephemeris to be used, default observable and whether TGD 
         * will be computed or not.
         *
         * The default ionospheric model will be set to NULL.
         *
         * This constructor is meant to be used when working with GNSS data structures in
         * order to set the basic parameters from the beginning.
         *
         * @param RxCoordinates Reference station coordinates.
         * @param dTropoModel   Tropospheric model to be used by default.
         * @param dEphemeris    EphemerisStore object to be used by default.
         * @param dObservable   Observable type to be used by default.
         * @param usetgd        Whether TGD will be used by default or not.
         *
         * @sa DataStructures.hpp.
         */
        ModeledReferencePR(const Position& RxCoordinates, TropModel& dTropoModel, EphemerisStore& dEphemeris, const TypeID& dObservable, bool usetgd = true) throw(Exception) : pDefaultIonoModel(NULL) { 
            InitializeValues();
            setInitialRxPosition(RxCoordinates);
            setDefaultTropoModel(dTropoModel);
            setDefaultObservable(dObservable);
            setDefaultEphemeris(dEphemeris);
            useTGD = usetgd;
        };


        /** Explicit constructor, taking as input reference station coordinates, 
         * ephemeris to be used, default observable and whether TGD will be computed 
         * or not.
         *
         * Both the tropospheric and ionospheric models will be set to NULL.
         *
         * This constructor is meant to be used when working with GNSS data structures in
         * order to set the basic parameters from the beginning.
         *
         * @param RxCoordinates Reference station coordinates.
         * @param dEphemeris    EphemerisStore object to be used by default.
         * @param dObservable   Observable type to be used by default.
         * @param usetgd        Whether TGD will be used by default or not.
         *
         * @sa DataStructures.hpp.
         */
        ModeledReferencePR(const Position& RxCoordinates, EphemerisStore& dEphemeris, const TypeID& dObservable, bool usetgd = true) throw(Exception) : pDefaultIonoModel(NULL), pDefaultTropoModel(NULL) { 
            InitializeValues();
            setInitialRxPosition(RxCoordinates);
            setDefaultObservable(dObservable);
            setDefaultEphemeris(dEphemeris);
            useTGD = usetgd;
        };


        /** Compute the modeled pseudoranges, given satellite ID's, pseudoranges and other data
         * @param Tr            Measured time of reception of the data.
         * @param Satellite     Vector of satellites; on successful return, satellites that
         *                      were excluded by the algorithm are marked by a negative
         *                      Satellite[i].prn
         * @param Pseudorange   Vector of raw pseudoranges (parallel to satellite), in meters.
         * @param Eph           EphemerisStore to be used.
         * @param pTropModel    Pointer to tropospheric model to be used. By default points to NULL.
         * @param pIonoModel    Pointer to ionospheric model to be used. By default points to NULL.
         * @param extraBiases   Vector of extra biases to be added to the model.
         *
         * @return
         *  Number of satellites with valid data
         *
         * @sa TropModel.hpp, IonoModelStore.hpp.
         */
        int Compute(const DayTime& Tr, Vector<SatID>& Satellite, Vector<double>& Pseudorange,
            const EphemerisStore& Eph, const Vector<double>& extraBiases, TropModel 
            *pTropModel=NULL, IonoModelStore *pIonoModel=NULL) throw(Exception);


        /// Compute the modeled pseudoranges, given satellite ID's, pseudoranges and other data
        int Compute(const DayTime& Tr, Vector<SatID>& Satellite, Vector<double>& Pseudorange,
            const EphemerisStore& Eph) throw(Exception);


        /// Compute the modeled pseudoranges, given satellite ID's, pseudoranges and other data
        int Compute(const DayTime& Tr, Vector<SatID>& Satellite, Vector<double>& Pseudorange,
            const EphemerisStore& Eph, TropModel *pTropModel) throw(Exception);


        /// Compute the modeled pseudoranges, given satellite ID's, pseudoranges and other data
        int Compute(const DayTime& Tr, Vector<SatID>& Satellite, Vector<double>& Pseudorange,
            const EphemerisStore& Eph, const Vector<double>& extraBiases, IonoModelStore
            *pIonoModel) throw(Exception);


        /// Compute the modeled pseudoranges, given satellite ID's, pseudoranges and other data
        int Compute(const DayTime& Tr, Vector<SatID>& Satellite, Vector<double>& Pseudorange,
            const EphemerisStore& Eph, IonoModelStore *pIonoModel) throw(Exception);


        /// Compute the modeled pseudoranges, given satellite ID's, pseudoranges and other data
        int Compute(const DayTime& Tr, Vector<SatID>& Satellite, Vector<double>& Pseudorange,
            const EphemerisStore& Eph, TropModel *pTropModel, IonoModelStore *pIonoModel)
            throw(Exception);


        /** Compute just one modeled pseudorange, given satellite ID's, pseudorange and other data
         * @param Tr            Measured time of reception of the data.
         * @param Satellite     ID's of satellite
         * @param Pseudorange   Pseudorange (parallel to satellite), in meters.
         * @param Eph           EphemerisStore to be used.
         * @param pTropModel    Pointer to tropospheric model to be used. By default points to NULL.
         * @param pIonoModel    Pointer to ionospheric model to be used. By default points to NULL.
         * @param extraBiases   Extra bias to be added to the model.
         *
         * @return
         *  1 if satellite has valid data
         *
         * @sa TropModel.hpp, IonoModelStore.hpp.
         */
        int Compute(const DayTime& Tr, SatID& Satellite, double& Pseudorange,
            const EphemerisStore& Eph, const double& extraBiases, TropModel *pTropModel=NULL,
            IonoModelStore *pIonoModel=NULL) throw(Exception);


        /** Returns a satTypeValueMap object, adding the new data generated when calling a modeling object.
         *
         * @param time      Epoch.
         * @param gData     Data object holding the data.
         */
        virtual satTypeValueMap& processModel(const DayTime& time, satTypeValueMap& gData) throw(Exception);


        /** Returns a gnnsSatTypeValue object, adding the new data generated when calling a modeling object.
         *
         * @param gData    Data object holding the data.
         */
        virtual gnssSatTypeValue& processModel(gnssSatTypeValue& gData) throw(Exception)
        {
            (*this).processModel(gData.header.epoch, gData.body);
            return gData;
        };


        /** Returns a gnnsRinex object, adding the new data generated when calling a modeling object.
         *
         * @param gData    Data object holding the data.
         */
        virtual gnssRinex& processModel(gnssRinex& gData) throw(Exception)
        {
            (*this).processModel(gData.header.epoch, gData.body);
            return gData;
        };


        /// Boolean variable indicating if SV instrumental delays (TGD) will be included  in results. It is true by default.
        bool useTGD;


        /// Method to get satellite elevation cut-off angle. By default, it is set to 10 degrees.
        virtual double getMinElev() const
        {
           return minElev;
        };


        /// Method to set satellite elevation cut-off angle. By default, it is set to 10 degrees.
        virtual void setMinElev(double newElevation)
        {
           minElev = newElevation;
        };


        /** Method to set the default ionospheric model.
         * @param dIonoModel    Ionospheric model to be used by default.
         */
        virtual void setDefaultIonoModel(IonoModelStore& dIonoModel)
        {
           pDefaultIonoModel = &dIonoModel;
        };


        /** Method to get a pointer to the default ionospheric model.
         */
        virtual IonoModelStore* getDefaultIonoModel() const
        {
           return pDefaultIonoModel;
        };


        /// Method to set a NULL ionospheric model.
        virtual void setNULLIonoModel()
        {
           pDefaultIonoModel = NULL;
        };


        /** Method to set the default tropospheric model.
         * @param dTropoModel    Tropospheric model to be used by default.
         */
        virtual void setDefaultTropoModel(TropModel& dTropoModel)
        {
           pDefaultTropoModel = &dTropoModel;
        };


        /** Method to get a pointer to the default tropospheric model.
         */
        virtual TropModel* getDefaultTropoModel() const
        {
           return pDefaultTropoModel;
        };


        /// Method to set a NULL tropospheric model.
        virtual void setNULLTropoModel()
        {
           pDefaultTropoModel = NULL;
        };


        /** Method to set the default extra biases.
         * @param eBiases    Vector with the default extra biases
         */
        virtual void setDefaultExtraBiases(Vector<double>& eBiases)
        {
           extraBiases = eBiases;
        };


        /** Method to set the default observable to be used when fed with GNSS data structures.
         * @param type      TypeID object to be used by default
         */
        virtual void setDefaultObservable(const TypeID& type)
        {
           defaultObservable = type;
        };


        /// Method to get the default observable being used with GNSS data structures.
        virtual TypeID getDefaultObservable() const
        {
           return defaultObservable;
        };


        /** Method to set the default EphemerisStore to be used with GNSS data structures.
         * @param ephem     EphemerisStore object to be used by default
         */
        virtual void setDefaultEphemeris(EphemerisStore& ephem)
        {
           pDefaultEphemeris = &ephem;
        };


        /** Method to get a pointer to the default EphemerisStore to be used with GNSS data structures.
         */
        virtual EphemerisStore* getDefaultEphemeris() const
        {
           return pDefaultEphemeris;
        };


        /// Destructor.
        virtual ~ModeledReferencePR() throw() {};


    protected:

        /// Pointer to default ionospheric model.
        IonoModelStore *pDefaultIonoModel;

        /// Pointer to default tropospheric model.
        TropModel *pDefaultTropoModel;

        /// Default observable to be used when fed with GNSS data structures.
        TypeID defaultObservable;

        /// Pointer to default EphemerisStore object when working with GNSS data structures.
        EphemerisStore* pDefaultEphemeris;

        /// Initialization method
        virtual void InitializeValues() throw(Exception) { 
            setInitialRxPosition();
            geometricRho(0);
            svClockBiases(0);
            svXvt(0);
            svTGD(0);
            svRelativity(0);
            ionoCorrections(0);
            tropoCorrections(0);
            modeledPseudoranges(0);
            prefitResiduals(0);
            extraBiases(0);
            availableSV(0);
            rejectedSV(0);
        };


        /** Method to set the initial (a priori) position of receiver.
         * @return
         *  0 if OK
         *  -1 if problems arose
         */
        virtual int setInitialRxPosition(const double& aRx, const double& bRx, const double& cRx, 
            Position::CoordinateSystem s=Position::Cartesian,
            GeoidModel *geoid=NULL) throw(GeometryException) 
        {
            try {
                Position rxpos(aRx, bRx, cRx, s, geoid);
                setInitialRxPosition(rxpos);
                return 0;
            }
            catch(GeometryException& e) {
                return -1;
            }
        };


        /// Method to set the initial (a priori) position of receiver.
        virtual int setInitialRxPosition(const Position& RxCoordinates) throw(GeometryException) 
        {
            try {
                (*this).rxPos = RxCoordinates;
                return 0;
            }
            catch(GeometryException& e) {
                return -1;
            }
        };


        /// Method to set the initial (a priori) position of receiver.
        virtual int setInitialRxPosition() throw(GeometryException) 
        {
            try {
                Position rxpos(0.0, 0.0, 0.0, Position::Cartesian, NULL);
                setInitialRxPosition(rxpos);
                return 0;
            }
            catch(GeometryException& e) {
                return -1;
            }
        };


        /// Method to get the tropospheric corrections.
        virtual double getTropoCorrections(TropModel *pTropModel, double elevation) throw() 
        {
            double tropoCorr(0.0);
            try {
                tropoCorr = pTropModel->correction(elevation);
                // Check validity
                if(!(pTropModel->isValid())) tropoCorr = 0.0;
            }
            catch(TropModel::InvalidTropModel& e) {
                tropoCorr = 0.0;
            }
            return tropoCorr;
        };


        /// Method to get the ionospheric corrections.
        virtual double getIonoCorrections(IonoModelStore *pIonoModel, DayTime Tr, Geodetic rxGeo, double elevation, double azimuth) throw() 
        {
            double ionoCorr(0.0);
            try {
                ionoCorr = pIonoModel->getCorrection(Tr, rxGeo, elevation, azimuth);
            }
            catch(IonoModelStore::NoIonoModelFound& e) {
                ionoCorr = 0.0;
            }
            return ionoCorr;
        };


        /// Method to get TGD corrections.
        virtual double getTGDCorrections(DayTime Tr, const EphemerisStore& Eph, SatID sat) throw() 
        {

            double TGDCorr(0.0);

            try {
                TGDCorr = (Eph.getTGD(sat, Tr));
            }
            catch(EphemerisStore::NoTGDFound& e) {
                TGDCorr = 0.0;
            }
            return TGDCorr;
        };


    }; // class ModeledReferencePR


    /// Input operator from gnssSatTypeValue to ModeledReferencePR.
    inline gnssSatTypeValue& operator>>(gnssSatTypeValue& gData, ModeledReferencePR& modRefPR) throw(Exception)
    {
            modRefPR.processModel(gData);
            return gData;
    }


    /// Input operator from gnssRinex to ModeledReferencePR.
    inline gnssRinex& operator>>(gnssRinex& gData, ModeledReferencePR& modRefPR) throw(Exception)
    {
            modRefPR.processModel(gData);
            return gData;
    }




   //@}

} // namespace

#endif
