//     ____   ______ __
//    / __ \ / ____// /
//   / /_/ // /    / /
//  / ____// /___ / /___   PixInsight Class Library
// /_/     \____//_____/   PCL 2.4.23
// ----------------------------------------------------------------------------
// pcl/Position.h - Released 2022-03-12T18:59:29Z
// ----------------------------------------------------------------------------
// This file is part of the PixInsight Class Library (PCL).
// PCL is a multiplatform C++ framework for development of PixInsight modules.
//
// Copyright (c) 2003-2022 Pleiades Astrophoto S.L. All Rights Reserved.
//
// Redistribution and use in both source and binary forms, with or without
// modification, is permitted provided that the following conditions are met:
//
// 1. All redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
//
// 2. All redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.
//
// 3. Neither the names "PixInsight" and "Pleiades Astrophoto", nor the names
//    of their contributors, may be used to endorse or promote products derived
//    from this software without specific prior written permission. For written
//    permission, please contact info@pixinsight.com.
//
// 4. All products derived from this software, in any form whatsoever, must
//    reproduce the following acknowledgment in the end-user documentation
//    and/or other materials provided with the product:
//
//    "This product is based on software from the PixInsight project, developed
//    by Pleiades Astrophoto and its contributors (https://pixinsight.com/)."
//
//    Alternatively, if that is where third-party acknowledgments normally
//    appear, this acknowledgment must be reproduced in the product itself.
//
// THIS SOFTWARE IS PROVIDED BY PLEIADES ASTROPHOTO AND ITS CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL PLEIADES ASTROPHOTO OR ITS
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, BUSINESS
// INTERRUPTION; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; AND LOSS OF USE,
// DATA OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
// ----------------------------------------------------------------------------

#ifndef __PCL_Position_h
#define __PCL_Position_h

/// \file pcl/Position.h

#include <pcl/Defs.h>

#include <pcl/AutoPointer.h>
#include <pcl/EphemerisFile.h>
#include <pcl/Matrix.h>

namespace pcl
{

// ----------------------------------------------------------------------------

/*!
 * \struct StarPosition
 * \brief Positional data of a star.
 *
 * This structure provides the data necessary for reduction of star positions
 * with the following member functions:
 *
 * Position::Geometric( const StarPosition& ) \n
 * Position::Astrometric( const StarPosition& ) \n
 * Position::Proper( const StarPosition& ) \n
 * Position::Apparent( const StarPosition& ) \n
 * Position::Intermediate( const StarPosition& )
 *
 * \ingroup solar_system_ephemerides
 */
struct PCL_CLASS StarPosition
{
   double    alpha = 0;    //!< ICRS right ascension in degrees.
   double    delta = 0;    //!< ICRS declination in degrees.
   double    muAlpha = 0;  //!< Proper motion in right ascension, mas/year * cos( delta ).
   double    muDelta = 0;  //!< Proper motion in declination, in mas/year.
   double    p = 0;        //!< Parallax in arcseconds.
   double    v = 0;        //!< Radial velocity in km/s, positive away from Earth.
   TimePoint t0 = TimePoint::J2000(); //!< Epoch of coordinates.

   /*!
    * Default constructor. See data members for default values.
    */
   StarPosition() = default;

   /*!
    * Copy constructor.
    */
   StarPosition( const StarPosition& x )
      : alpha( x.alpha )
      , delta( x.delta )
      , muAlpha( x.muAlpha )
      , muDelta( x.muDelta )
      , p( x.p )
      , v( x.v )
      , t0( x.t0 )
   {
   }

   /*!
    * Copy assignment operator. Returns a reference to this object.
    */
   StarPosition& operator =( const StarPosition& x )
   {
      alpha = x.alpha;
      delta = x.delta;
      muAlpha = x.muAlpha;
      muDelta = x.muDelta;
      p = x.p;
      v = x.v;
      t0 = x.t0;
      return *this;
   }

   /*!
    * Memberwise constructor.
    *
    * \param ra               Right ascension equatorial coordinate in degrees.
    *                         Can be specified in the range [-180,+180] or
    *                         [0,360) indistinctly.
    *
    * \param dec              Declination equatorial coordinate in degrees.
    *                         Should be in the [-90,+90] range.
    *
    * \param properMotionRA   Proper motion in right ascension, in mas/year,
    *                         measured on a great circle, that is, the
    *                         specified value must be the proper motion in R.A.
    *                         multiplied by the cosine of the declination. Zero
    *                         by default.
    *
    * \param properMotionDec  Proper motion in declination, in mas/year. Zero
    *                         by default.
    *
    * \param parallax         Parallax in arcseconds. Zero by default.
    *
    * \param radialVelocity   Radial velocity in km/s, positive away from the
    *                         Earth. Zero by default.
    *
    * \param epoch            Epoch of catalog coordinates. J2000.0 by default.
    *
    * Positions and proper motions must be referred to ICRS/J2000.0.
    *
    * If out-of-range coordinates are specified, their values will be
    * constrained to their proper ranges: right ascension to [0&deg;,360&deg;)
    * and declination to [-90&deg;,+90&deg;].
    */
   StarPosition( double ra, double dec,
                 double properMotionRA = 0, double properMotionDec = 0,
                 double parallax = 0,
                 double radialVelocity = 0,
                 TimePoint epoch = TimePoint::J2000() )
      : alpha( ((ra = Mod( ra, 360.0 )) < 0) ? ra + 360 : ra )
      , delta( Range( dec, -90.0, +90.0 ) )
      , muAlpha( properMotionRA )
      , muDelta( properMotionDec )
      , p( parallax )
      , v( radialVelocity )
      , t0( epoch )
   {
   }

private:

   uint64 m_uniqueId = UniqueId();

   static uint64 UniqueId();

   friend class PCL_CLASS Position;
};

// ----------------------------------------------------------------------------

/*!
 * \struct ObserverPosition
 * \brief Geodetic coordinates of a terrestrial observer.
 *
 * This structure provides the data necessary to calculate topocentric places
 * of solar system bodies and stars.
 *
 * \ingroup solar_system_ephemerides
 */
struct PCL_CLASS ObserverPosition
{
   double lambda = 0;             //!< Geodetic longitude in degrees.
   double phi = 0;                //!< Geodetic latitude in degrees.
   double h = 0;                  //!< Geodetic height in meters.
   double a = ea_eq_radius_IAU2009;   //!< Equatorial radius of the reference spheroid in meters.
   double f = ea_flattening_IERS2010; //!< Flattening of the reference spheroid.
   Vector r0 = Vector( 0, 0, 0 ); //!< Geocentric rectangular coordinates of the center of the regional spheroid, in meters.
   bool   cioBased = false;       //!< Whether to compute CIO-based or equinox-based topocentric coordinates.

   /*!
    * Equatorial radius of Earth in meters, IAU 2009/2012.
    */
   constexpr static double ea_eq_radius_IAU2009 = 6378136.6;

   /*!
    * Flattening of Earth, IERS 2010.
    */
   constexpr static double ea_flattening_IERS2010 = 1/298.25642;

   /*!
    * Default constructor.
    *
    * Constructs an %ObserverPosition object for a fictitious observer at the
    * geocenter. See data members for specific default values.
    */
   ObserverPosition() = default;

   /*!
    * Copy constructor.
    */
   ObserverPosition( const ObserverPosition& ) = default;

   /*!
    * Move constructor.
    */
   ObserverPosition( ObserverPosition&& ) = default;

   /*!
    * Copy assignment operator.
    */
   ObserverPosition& operator =( const ObserverPosition& ) = default;

   /*!
    * Move assignment operator.
    */
   ObserverPosition& operator =( ObserverPosition&& ) = default;

   /*!
    * Memberwise constructor.
    *
    * \param longitude        Geodetic longitude in degrees.
    *
    * \param latitude         Geodetic latitude in degrees.
    *
    * \param height           Geodetic height in meters. Zero by default.
    *
    * \param equatorialRadius Equatorial radius of the reference spheroid in
    *                         meters. The default value is the IAU 2009/2012
    *                         equatorial radius of Earth: 6,378,136.6 meters.
    *
    * \param flattening       Flattening of the reference spheroid. The default
    *                         value is the IERS 2010 flattening of Earth:
    *                         1/298.25642.
    *
    * \param regionalCenter   Geocentric rectangular coordinates of the center
    *                         of the regional spheroid, in meters. A zero 3-D
    *                         vector (that is, the geocenter) by default.
    *
    * \param useCIO           Whether to compute CIO-based or equinox-based
    *                         topocentric coordinates. False (equinox-based) by
    *                         default.
    *
    * Typically, the values used should be WGS84 coordinates (for example, as
    * distributed by GPS) or ITRF coordinates (both systems close together at
    * the level of a few centimeters).
    *
    * Geographic longitudes grow eastward, so longitudes are positive to the
    * east and negative to the west of the central meridian. For ease of
    * calculation, this constructor converts longitudes to the
    * [0&deg;,360&deg;) range.
    *
    * If out-of-range coordinates are specified, their values will be
    * constrained to their proper ranges: longitude to [0&deg;,360&deg;), and
    * latitude to [-90&deg;,+90&deg;].
    */
   ObserverPosition( double longitude, double latitude,
                     double height = 0,
                     double equatorialRadius = ea_eq_radius_IAU2009,
                     double flattening = ea_flattening_IERS2010,
                     const Vector& regionalCenter = Vector( 0, 0, 0 ),
                     bool useCIO = false )
      : lambda( ((longitude = Mod( longitude, 360.0 )) < 0) ? longitude + 360 : longitude )
      , phi( Range( latitude, -90.0, +90.0 ) )
      , h( Max( 0.0, height ) )
      , a( Max( 0.0, equatorialRadius ) )
      , f( Max( 0.0, flattening ) )
      , r0( regionalCenter )
      , cioBased( useCIO )
   {
   }
};

// ----------------------------------------------------------------------------

/*!
 * \class Position
 * \brief Reduction of planetary and stellar positions.
 *
 * This class implements algorithms for reduction of positions of solar system
 * bodies and stars. It allows for calculation of geometric, astrometric,
 * proper, apparent and intermediate places, including geocentric and
 * topocentric coordinates.
 *
 * The implemented vector astrometry and ephemeris calculation algorithms are
 * rigorous and compliant with current IAU and IERS resolutions. Both
 * equinox-based and CIO-based paradigms have been implemented for calculation
 * of positions that depend on Earth's rotation. The apparent and intermediate
 * places include the following corrections:
 *
 * \li Light-travel time for solar system bodies.
 *
 * \li Space motion for stars, including parallax, radial velocity and proper
 * motions, with corrections for the relativistic Doppler effect that takes
 * into account the change in light-travel time for stars.
 *
 * \li Relativistic deflection of light due to solar gravitation.
 *
 * \li Aberration of light, relativistic model.
 *
 * \li %Frame bias, precession and nutation. (equinox-based and CIO-based).
 *
 * \li Accurate topocentric places with polar motion corrections.
 *
 * Vector components are expressed in astronomical units (au) for stars and all
 * solar system bodies except the Moon, for which positions are given in
 * kilometers.
 *
 * As of writing this documentation (October 2018), the IAU 2006/2000A
 * precession-nutation theory is implemented (adjusted model with corrections
 * to nutation angles, IAU 2006/2000A<sub>R</sub>). The standard fundamental
 * ephemerides are JPL's DE438/LE438.
 *
 * Most of the reference publications and material used are cited in source
 * code comments and the documentation. The main authoritative resource is:
 *
 * Urban, Sean E., Kenneth Seidelmann, P., ed. (2013), <em>The Explanatory
 * Supplement to the Astronomical Almanac</em> 3rd Edition.
 *
 * \ingroup solar_system_ephemerides
 */
class PCL_CLASS Position
{
public:

   /*!
    * Constructs a %Position object initialized for the specified time of
    * calculation \a t in the specified \a timescale.
    *
    * The supported time scales are:
    *
    * <table border="1" cellpadding="4" cellspacing="0">
    * <tr><td>\b TT</td>   <td>Terrestrial Time. This is the default
    *                      timescale.</td></tr>
    *
    * <tr><td>\b TDB</td>  <td>Barycentric Dynamical Time.</td></tr>
    *
    * <tr><td>\b Teph</td> <td>Ephemeris time, as defined by JPL DE/LE
    *                      numerical integrations. For all purposes, this is
    *                      equivalent to TDB.</td></tr>
    *
    * <tr><td>\b UTC</td>  <td>Coordinated Universal Time.</td></tr>
    *
    * <tr><td>\b TAI</td>  <td>Atomic International Time.</td></tr>
    *
    * <tr><td>\b UT1</td>  <td>Universal Time.</td></tr>
    *
    * <tr><td>\b UT</td>   <td>Universal Time (same as UT1).</td></tr>
    * </table>
    *
    * \a timescale string values are considered case-sensitive.
    *
    * All necessary timescale conversions to compute ephemerides and reduction
    * of positions are performed automatically.
    */
   Position( TimePoint t, const IsoString& timescale = "TT" );

   /*!
    * Copy constructor.
    */
   Position( const Position& ) = default;

   /*!
    * Move constructor.
    */
   Position( Position&& ) = default;

   /*!
    * Copy assignment operator. Returns a reference to this object.
    */
   Position& operator =( const Position& ) = default;

   /*!
    * Move assignment operator. Returns a reference to this object.
    */
   Position& operator =( Position&& ) = default;

   /*!
    * Computes the true position of a solar system body.
    *
    * The components of the returned vector are the geocentric or topocentric
    * rectangular equatorial coordinates for the calculation instant defined by
    * this object in the TT timescale, <em>without accounting for light-travel
    * time,</em> for the body defined by the specified ephemeris handle \a H.
    *
    * This function calls Geometric( EphemerisFile::Handle& ) internally to
    * compute, if necessary, the geometric position with correction for light
    * time, that is, no separate calculation routine is implemented for true
    * positions. The returned vector is only useful to compute the true
    * geocentric or topocentric distance, and for verification purposes.
    */
   Vector True( EphemerisFile::Handle& H )
   {
      (void)Geometric( H );
      return m_U0;
   }

   /*!
    * Computes the geometric position of a star.
    *
    * This function has been implemented for completeness. It is a synonym for
    * Geometric( const StarPosition& ). There are no known 'true' positions of
    * stars, since their light-travel time is implicitly included in the space
    * motion equations.
    */
   Vector True( const StarPosition& S )
   {
      (void)Geometric( S );
      return m_U0;
   }

   /*!
    * Computes the true distance of a solar system body.
    *
    * The true distance is the actual distance from the body to the observer,
    * geocentric or topocentric, at the instant of calculation. This excludes
    * the light-travel time correction.
    */
   double TrueDistance( EphemerisFile::Handle& H )
   {
      return True( H ).L2Norm();
   }

   /*!
    * Computes the true distance of a star.
    *
    * The returned value is just the norm of the geometric position vector,
    * that is:
    *
    * \code Geometric( S ).L2Norm() \endcode
    *
    * This should be an actual distance in au only for stars with known
    * parallaxes. For stars where the parallax is unknown or undefined, this
    * value is meaningless because in such cases position vectors are unit
    * vectors, whose components are also known as <em>direction cosines.</em>
    */
   double TrueDistance( const StarPosition& S )
   {
      return True( S ).L2Norm();
   }

   /*!
    * Computes the geometric position of a solar system body.
    *
    * The components of the returned vector are the geocentric or topocentric
    * rectangular equatorial coordinates for the instant of calculation defined
    * by this object in the TT timescale, accounting for light-travel time, for
    * the body defined by the specified ephemeris handle \a H.
    *
    * The implemented reduction algorithm includes just the correction for
    * light-travel time, but no corrections for light deflection, annual
    * aberration, nutation, or precession. The position so calculated allows to
    * plot the specified body directly on an existing sky chart referred to
    * GCRS/J2000.0. Note however, that for generation of new graphical
    * representations for a given date using star catalog data, astrometric or
    * proper places should be used instead.
    */
   Vector Geometric( EphemerisFile::Handle& H );

   /*!
    * Computes the geometric position of a star.
    *
    * The components of the returned vector are the geocentric or topocentric
    * rectangular equatorial coordinates for the instant of calculation defined
    * by this object in the TT timescale, for the positional star data defined
    * by the specified object \a S.
    *
    * The implemented reduction algorithm includes just the corrections for
    * space motion: parallax, radial velocity and proper motions, when the
    * corresponding data items are nonzero in the specified object \a S. The
    * space motion vector includes terms to account for the relativistic
    * Doppler effect.
    */
   Vector Geometric( const StarPosition& S );

   /*!
    * Computes the astrometric place of a solar system body.
    *
    * The returned vector is the astrometric place of the object defined by the
    * specified ephemeris handle \a H in geocentric or topocentric rectangular
    * equatorial coordinates, calculated for the instant defined by this object
    * in the TT timescale.
    *
    * The implemented reduction algorithm includes the following corrections:
    *
    * \li Light-travel time.
    *
    * \li Relativistic deflection of light due to solar gravitation (except
    * for the Sun, the Moon, and any object closer from Earth than the Sun at
    * the time of observation).
    *
    * An astrometric place does not include annual aberration, nutation and
    * precession corrections. Hence it is referred to an 'hybrid' reference
    * system, but similar to GCRS J2000.0.
    */
   Vector Astrometric( EphemerisFile::Handle& H );

   /*!
    * Computes the astrometric place of a star.
    *
    * The returned vector is the astrometric place calculated for the
    * positional star data defined by the specified object \a S. Its components
    * are geocentric or topocentric rectangular equatorial coordinates,
    * calculated for the instant defined by this object in the TT timescale.
    *
    * The implemented reduction algorithm includes the following corrections:
    *
    * \li Space motion, including parallax, radial velocity and proper motions,
    * with corrections for the relativistic Doppler effect.
    *
    * \li Relativistic deflection of light due to solar gravitation.
    *
    * An astrometric place does not include annual aberration, nutation and
    * precession corrections. Hence it is referred to an 'hybrid' reference
    * system, but similar to GCRS J2000.0.
    */
   Vector Astrometric( const StarPosition& S );

   /*!
    * Computes the proper place of a solar system body.
    *
    * The returned vector is the proper place of the object defined by the
    * specified ephemeris handle \a H in geocentric or topocentric rectangular
    * equatorial coordinates, calculated for the instant defined by this object
    * in the TT timescale.
    *
    * The implemented reduction algorithm includes the following corrections:
    *
    * \li Light-travel time.
    *
    * \li Relativistic deflection of light due to solar gravitation (except
    * for the Sun, the Moon, and any object closer from Earth than the Sun at
    * the time of observation.
    *
    * \li Aberration of light, including relativistic terms.
    *
    * A proper place does not include nutation and precession corrections,
    * hence it is referred to the reference coordinate system: GCRS J2000.0.
    */
   Vector Proper( EphemerisFile::Handle& H );

   /*!
    * Computes the proper place of a star.
    *
    * The returned vector is the proper place calculated for the positional
    * star data defined by the specified object \a S. Its components are
    * geocentric or topocentric rectangular equatorial coordinates, calculated
    * for the instant defined by this object in the TT timescale.
    *
    * The implemented reduction algorithm includes the following corrections:
    *
    * \li Space motion, including parallax, radial velocity and proper motions,
    * with corrections for the relativistic Doppler effect.
    *
    * \li Relativistic deflection of light due to solar gravitation.
    *
    * \li Aberration of light, including relativistic terms.
    *
    * A proper place does not include nutation and precession corrections,
    * hence it is referred to the reference coordinate system: GCRS J2000.0.
    */
   Vector Proper( const StarPosition& S );

   /*!
    * Computes the apparent place of a solar system body.
    *
    * The returned vector is the apparent place of the object defined by the
    * specified ephemeris handle \a H in geocentric or topocentric rectangular
    * equatorial coordinates, calculated for the instant defined by this object
    * in the TT timescale.
    *
    * The implemented reduction algorithm includes the following corrections:
    *
    * \li Light-travel time.
    *
    * \li Relativistic deflection of light due to solar gravitation (except
    * for the Sun, the Moon, and any object closer from Earth than the Sun at
    * the time of observation.
    *
    * \li Aberration of light, including relativistic terms.
    *
    * \li %Frame bias, precession and nutation. The origin of right ascension is
    * the true equinox of date.
    *
    * \note The declination coordinate is identical in both equinox-based
    * (apparent) and CIO-based (intermediate) positions. Only the origin of
    * right ascensions differs between both systems.
    *
    * \warning If a valid observer location has been defined by calling the
    * SetObserver() member function, and the specified ObserverPosition
    * structure requires CIO-based transformations (see the
    * ObserverPosition::cioBased member), this function will throw an Error
    * exception.
    */
   Vector Apparent( EphemerisFile::Handle& H );

   /*!
    * Computes the apparent place of a star.
    *
    * The returned vector is the apparent place calculated for the positional
    * star data defined by the specified object \a S. Its components are
    * geocentric or topocentric rectangular equatorial coordinates, calculated
    * for the instant defined by this object in the TT timescale.
    *
    * The implemented reduction algorithm includes the following corrections:
    *
    * \li Space motion, including parallax, radial velocity and proper motions,
    * with corrections for the relativistic Doppler effect.
    *
    * \li Relativistic deflection of light due to solar gravitation.
    *
    * \li Aberration of light, including relativistic terms.
    *
    * \li %Frame bias, precession and nutation. The origin of right ascension is
    * the true equinox of date.
    *
    * \note The declination coordinate is identical in both equinox-based
    * (apparent) and CIO-based (intermediate) positions. Only the origin of
    * right ascensions differs between both systems.
    *
    * \warning If a valid observer location has been defined by calling the
    * SetObserver() member function, and the specified ObserverPosition
    * structure requires CIO-based transformations (see the
    * ObserverPosition::cioBased member), this function will throw an Error
    * exception.
    */
   Vector Apparent( const StarPosition& S );

   /*!
    * Computes the intermediate place of a solar system body.
    *
    * The returned vector is the intermediate place of the object defined by
    * the specified ephemeris handle \a H in geocentric or topocentric
    * rectangular equatorial coordinates, calculated for the instant defined by
    * this object in the TT timescale.
    *
    * The implemented reduction algorithm includes the following corrections:
    *
    * \li Light-travel time.
    *
    * \li Relativistic deflection of light due to solar gravitation (except
    * for the Sun, the Moon, and any object closer from Earth than the Sun at
    * the time of observation.
    *
    * \li Aberration of light, including relativistic terms.
    *
    * \li %Frame bias, precession and nutation. The origin of right ascension is
    * the Celestial Intermediate Origin (CIO), following the IAU
    * recommendations since January 2003.
    *
    * \note The declination coordinate is identical in both equinox-based
    * (apparent) and CIO-based (intermediate) positions. Only the origin of
    * right ascensions differs between both systems.
    *
    * \warning If a valid observer location has been defined by calling the
    * SetObserver() member function, and the specified ObserverPosition
    * structure requires equinox-based transformations (see the
    * ObserverPosition::cioBased member), this function will throw an Error
    * exception.
    */
   Vector Intermediate( EphemerisFile::Handle& H );

   /*!
    * Computes the intermediate place of a star.
    *
    * The returned vector is the intermediate place calculated for the
    * positional star data defined by the specified object \a S. Its components
    * are geocentric or topocentric rectangular equatorial coordinates,
    * calculated for the instant defined by this object in the TT timescale.
    *
    * The implemented reduction algorithm includes the following corrections:
    *
    * \li Space motion, including parallax, radial velocity and proper motions,
    * with corrections for the relativistic Doppler effect.
    *
    * \li Relativistic deflection of light due to solar gravitation.
    *
    * \li Aberration of light, including relativistic terms.
    *
    * \li %Frame bias, precession and nutation. The origin of right ascension is
    * the Celestial Intermediate Origin (CIO), following the IAU
    * recommendations since January 2003.
    *
    * \note The declination coordinate is identical in both equinox-based
    * (apparent) and CIO-based (intermediate) positions. Only the origin of
    * right ascensions differs between both systems.
    *
    * \warning If a valid observer location has been defined by calling the
    * SetObserver() member function, and the specified ObserverPosition
    * structure requires equinox-based transformations (see the
    * ObserverPosition::cioBased member), this function will throw an Error
    * exception.
    */
   Vector Intermediate( const StarPosition& S );

   /*!
    * Defines the geodetic positional and reference data necessary for
    * calculation of topocentric positions of solar system objects and stars.
    *
    * By default, an instance of the %Position class calculates geocentric
    * coordinates. After calling this member function, subsequently calculated
    * geometric, proper, astrometric, apparent and intermediate places will be
    * topocentric, that is, will be referred to the location of the observer
    * with respect to the center of the Earth.
    *
    * By calling this member function, all previously computed positional data
    * will be erased with the exception of fundamental ephemerides and existing
    * bias-precession-nutation matrices, which can always be preserved.
    *
    * The ObserverPosition::cioBased data member of the specified \a observer
    * structure selects the celestial system and paradigm to be used in the
    * calculation of positions that depend on Earth's rotation. If the cioBased
    * member is true, equinox-based places cannot be calculated, and any of the
    * Apparent() member functions will throw an exception if called.
    * Conversely, if cioBased is false, CIO-based places cannot be calculated
    * and no call to an Intermediate() member function will be allowed.
    *
    * If polar motion corrections are enabled, the position of the CIP with
    * respect to the ITRS is interpolated from the global CIP_ITRS database, if
    * it provides data for the current time of calculation. In such case, polar
    * motion is taken into account in the calculation of the observer's
    * geocentric position and velocity. For the geocentric velocity a standard
    * constant value for the rotation rate of the Earth is used; the velocity
    * component due to precession and nutation is not taken into account since
    * its effect is negligible. See the IsPolarMotionEnabled() and CIP_ITRS()
    * member functions for more information and additional references.
    */
   void SetObserver( const ObserverPosition& observer );

   /*!
    * Removes the current observer, if a valid one has been defined by a
    * previous call to SetObserver(). In such case, all previously computed
    * positional data will be invalidated, with the exception of fundamental
    * ephemerides and existing bias-precession-nutation matrices.
    *
    * If no observer has been defined for this object, calling this member
    * function has no effect.
    *
    * After calling this member function, all subsequently calculated
    * geometric, proper, astrometric, apparent and intermediate places will be
    * geocentric.
    */
   void SetGeocentric();

   /*!
    * Returns the current observer. If no observer has been defined for this
    * object, returns a default-constructed structure for a fictitious observer
    * at the geocenter.
    */
   ObserverPosition Observer() const
   {
      if ( m_observer )
         return *m_observer;
      return ObserverPosition();
   }

   /*!
    * Returns true iff a valid observer location has been defined for this
    * object in a previous call to SetObserver().
    */
   bool IsTopocentric() const
   {
      return m_observer;
   }

   /*!
    * Returns true iff topocentric places take into account polar motion
    * corrections to compute the geocentric position and velocity of the
    * observer. This involves calculation of CIP coordinates with respect to
    * the ITRS, as well as access to a database of CIP/ITRS positions. See the
    * CIP_ITRS() member function for more details.
    *
    * Polar motion introduces changes at the mas level for calculation of
    * topocentric coordinates of the Moon. For the rest of objects, the effect
    * of polar motion corrections is completely negligible. For topocentric
    * positions of the Moon, polar motion can be necessary to achieve the
    * highest accuracy, but in such case one may also have to take into account
    * a regional geoid referred to the Earth's reference ellipsoid. See the
    * ObserverPosition structure.
    */
   bool IsPolarMotionEnabled() const
   {
      return m_usePolarMotion;
   }

   /*!
    * Enables polar motion corrections for calculation of topocentric places.
    * See the CIP_ITRS() and IsPolarMotionEnabled() functions for more details.
    */
   void EnablePolarMotion( bool enable = true )
   {
      m_usePolarMotion = enable;
   }

   /*!
    * Disables polar motion corrections for calculation of topocentric places.
    * See the CIP_ITRS() and IsPolarMotionEnabled() functions for more details.
    */
   void DisablePolarMotion( bool disable = true )
   {
      EnablePolarMotion( !disable );
   }

   /*!
    * Returns the time of calculation for this object in the Barycentric
    * Dynamical Time (TDB) timescale.
    *
    * The times of calculation in different timescales are calculated by the
    * class constructor.
    */
   TimePoint TDB() const
   {
      return m_t;
   }

   /*!
    * Returns the time of calculation for this object in the ephemeris
    * timescale defined by the JPL DE/LE numerical integration. For all
    * purposes this is equivalent to TDB.
    *
    * The times of calculation in different timescales are calculated by the
    * class constructor.
    */
   TimePoint Teph() const
   {
      return m_t;
   }

   /*!
    * Returns the time of calculation for this object in the Terrestrial Time
    * (TT) timescale.
    *
    * The times of calculation in different timescales are calculated by the
    * class constructor.
    */
   TimePoint TT() const
   {
      return m_tt;
   }

   /*!
    * Returns the time of calculation for this object in the Universal Time
    * (UT1) timescale.
    *
    * The times of calculation in different timescales are calculated by the
    * class constructor.
    */
   TimePoint UT1() const
   {
      return m_ut1;
   }

   /*!
    * Returns the ICRS barycentric position of the Earth (barycentric
    * rectangular equatorial coordinates), computed for the TDB time of
    * calculation by the class constructor. The components of the
    * returned vector are expressed in au.
    */
   Vector BarycentricPositionOfEarth() const
   {
      return m_Eb;
   }

   /*!
    * Returns the ICRS barycentric velocity of the Earth (barycentric
    * rectangular equatorial coordinates), computed for the TDB time of
    * calculation by the class constructor. The components of the
    * returned vector are expressed in au/day.
    */
   Vector BarycentricVelocityOfEarth() const
   {
      return m_Edb;
   }

   /*!
    * Returns the ICRS barycentric position of the Sun (barycentric rectangular
    * equatorial coordinates), computed for the TDB time of calculation by the
    * class constructor. The components of the returned vector are in au.
    */
   Vector BarycentricPositionOfSun() const
   {
      return m_Sb;
   }

   /*!
    * Returns the ICRS heliocentric position of the Earth (heliocentric
    * rectangular equatorial coordinates), computed for the TDB time of
    * calculation by the class constructor. The components of the
    * returned vector are expressed in au.
    */
   Vector HeliocentricPositionOfEarth() const
   {
      return m_Eh;
   }

   /*!
    * Calculates all parameters and data structures necessary for equinox-based
    * reduction of positions.
    *
    * This member function calculates the following structures:
    *
    * \li Precession+bias angles, IAU 2006 precession model, Fukushima-Williams
    * parameterization. See ESAsA sections 6.6.2.2 and 7.2.5.1.
    *
    * \li Mean obliquity of the ecliptic, IAU 2006 precession model. See ESAsA
    * section 7.2.5.1.
    *
    * \li Nutation angles, IAU 2006/2000A_R nutation model. See ESAsA section
    * 6.6.1.
    *
    * \li Combined bias-precession-nutation matrix, equinox-based. See ESAsA
    * sections 6.7 and 7.2.5.1.
    *
    * \li Position of the Celestial Intermediate Pole (CIP). ESAsA section 6.7.
    *
    * \li Celestial Intermediate Origin (CIO) locator. ESAsA section 6.7.
    *
    * \li Equation of the origins (EO). See Wallace, P. & Capitaine, N., 2006,
    * Astron.Astrophys. 459, 981, and ESAsA section 6.4.
    *
    * \li Earth rotation angle (ERA) for the UT1 time of calculation. See IERS
    * Technical Note No. 32, 2003, Section 5.4.4.
    *
    * \li Greenwich Apparent Sidereal Time (GAST), IAU 2006. ESAsA 6.8.5.
    *
    * Since all of these items depend exclusively on time, they are computed
    * only once in the first call to this function, and subsequent calls will
    * have no effect.
    *
    * Normally, you don't have to call this function directly because it is
    * invoked automatically when necessary by the different position reduction
    * routines.
    */
   void InitEquinoxBasedParameters();

   /*!
    * Calculates all parameters and data structures necessary for CIO-based
    * reduction of positions.
    *
    * This member function starts by calling InitEquinoxBasedParameters(), so
    * it implicitly calculates all equinox-based parameters. Then it calculates
    * the CIO-based combined bias-precession-nutation matrix. See ESAsA
    * sections 6.7 and 7.2.5.2.
    *
    * Since all of these items depend exclusively on time, they are computed
    * only once in the first call to this function, and subsequent calls will
    * have no effect.
    *
    * Normally, you don't have to call this function directly because it is
    * invoked automatically when necessary by the different position reduction
    * routines.
    */
   void InitCIOBasedParameters();

   /*!
    * Returns the equinox-based combined bias-precession-nutation matrix.
    *
    * This member function calls InitEquinoxBasedParameters() to ensure that
    * all data required for equinox-based reduction of positions is available.
    */
   Matrix EquinoxBiasPrecessionNutationMatrix()
   {
      InitEquinoxBasedParameters();
      return m_M;
   }

   /*!
    * Returns the inverse of the equinox-based combined
    * bias-precession-nutation matrix.
    *
    * This member function calls InitEquinoxBasedParameters() to ensure that
    * all data required for equinox-based reduction of positions is available.
    */
   Matrix EquinoxBiasPrecessionNutationInverseMatrix()
   {
      InitEquinoxBasedParameters();
      return m_Minv;
   }

   /*!
    * Returns the CIO-based combined bias-precession-nutation matrix.
    *
    * This member function calls InitCIOBasedParameters() to ensure that
    * all data required for equinox-based and CIO-based reduction of positions
    * is available.
    */
   Matrix CIOBiasPrecessionNutationMatrix()
   {
      InitCIOBasedParameters();
      return m_C;
   }

   /*!
    * Returns the inverse of the CIO-based combined bias-precession-nutation
    * matrix.
    *
    * This member function calls InitCIOBasedParameters() to ensure that
    * all data required for equinox-based and CIO-based reduction of positions
    * is available.
    */
   Matrix CIOBiasPrecessionNutationInverseMatrix()
   {
      InitCIOBasedParameters();
      return m_Cinv;
   }

   /*!
    * Coordinates of the Celestial Intermediate Pole (CIP) in the GCRS, in
    * radians.
    *
    * This member function calls InitEquinoxBasedParameters() to ensure that
    * all data required for equinox-based reduction of positions is available.
    */
   Vector CIP()
   {
      InitEquinoxBasedParameters();
      return Vector( m_X, m_Y );
   }

   /*!
    * Coordinates of the Celestial Intermediate Pole (CIP) in the ITRS.
    *
    * If possible, this member function returns an interpolated value from the
    * global CIP_ITRS database, which will be loaded and parsed upon the first
    * call to this function as a thread-safe procedure. See
    * EphemerisFile::CIP_ITRSDataFilePath() and
    * EphemerisFile::OverrideCIP_ITRSDataFilePath() for more information. See
    * also IsPolarMotionEnabled() for some practical considerations.
    *
    * Otherwise, if the time of calculation for this object falls outside the
    * CIP_ITRS database time span, this function will return a two-dimensional
    * vector with zero components.
    *
    * The components of the returned 2-D vector are the coordinates
    * x<sub>p</sub>, y<sub>p</sub> of the CIP with respect to the ITRS,
    * expressed in radians.
    */
   Vector CIP_ITRS() const;

   /*!
    * The Celestial Intermediate Origin (CIO) locator, in radians.
    *
    * This member function calls InitEquinoxBasedParameters() to ensure that
    * all data required for equinox-based reduction of positions is available.
    */
   double CIO()
   {
      InitEquinoxBasedParameters();
      return m_s;
   }

   /*!
    * Equation of the origins, expressed in radians.
    *
    * This member function calls InitEquinoxBasedParameters() to ensure that
    * all data required for equinox-based reduction of positions is available.
    */
   double EO()
   {
      InitEquinoxBasedParameters();
      return m_EO;
   }

   /*!
    * Earth rotation angle, expressed in radians.
    *
    * This member function calls InitEquinoxBasedParameters() to ensure that
    * all data required for equinox-based reduction of positions is available.
    */
   double ERA()
   {
      InitEquinoxBasedParameters();
      return m_ERA;
   }

   /*!
    * Greenwich apparent sidereal time, expressed in radians.
    *
    * This member function calls InitEquinoxBasedParameters() to ensure that
    * all data required for equinox-based reduction of positions is available.
    */
   double GAST()
   {
      InitEquinoxBasedParameters();
      return m_GAST;
   }

   /*!
    * Mean obliquity of the ecliptic, in radians.
    */
   double EpsA() const
   {
      // Mean obliquity of the ecliptic, IAU 2006 precession model.
      if ( m_M.IsEmpty() )
         return AsRad( Poly( m_TT, { 84381.406, -46.836769, -0.0001831, 0.00200340, -0.000000576, -0.0000000434 } ) );
      return m_epsa;
   }

   /*!
    * Returns the nutation angles in radians as a point p, where \e p.x is the
    * nutation in longitude and \e p.y is the nutation in obliquity.
    *
    * This member function calls InitEquinoxBasedParameters() to ensure that
    * all data required for equinox-based reduction of positions is available.
    */
   DPoint NutationAngles()
   {
      InitEquinoxBasedParameters();
      return DPoint( m_dpsi, m_deps );
   }

   /*!
    * Returns true iff the apparent visual magnitude of the object represented
    * by the specified handle \a H can be calculated with the current
    * implementation, at the calculation time defined by this instance.
    *
    * Currently apparent visual magnitudes can be calculated for the following
    * solar system bodies:
    *
    * \li Objects providing valid H and G parameters (absolute magnitude and
    * slope coefficient). This happens for most asteroids included in standard
    * XEPH files.
    *
    * \li Mercury, Venus, Mars, Jupiter, Saturn, Uranus, Neptune and Pluto.
    *
    * \li The four Galilean satellites of Jupiter: Io, Europa, Ganymede and
    * Callisto.
    *
    * \sa ApparentVisualMagnitude()
    */
   bool CanComputeApparentVisualMagnitude( const EphemerisFile::Handle& H ) const;

   /*!
    * Returns the observed visual magnitude of a solar system body.
    *
    * For objects with known H and G values (absolute magnitude and slope
    * parameters, respectively; see EphemerisFile::Handle::H() and
    * EphemerisFile::Handle::G()), the apparent visual magnitude is calculated
    * applying the algorithm for minor planets described in Bowell et al.
    * (1989). See also The Explanatory Supplement, Section 10.4.3.
    *
    * For Mercury, Venus, Mars, Jupiter, Saturn and Neptune, we apply the
    * equations described in the following paper:
    *
    * Anthony Mallama, James L. Hilton, <em>Computing Apparent Planetary
    * Magnitudes for The Astronomical Almanac</em>, revised 2018 June 21.
    *
    * As of writing this documentation, the above paper is available online at:
    * https://arxiv.org/pdf/1808.01973.pdf
    *
    * For Saturn, we compute the apparent visual magnitude taking into account
    * the planet's rings.
    *
    * For Uranus, Pluto and the Galilean satellites of Jupiter, data from
    * various sources are taken from Table 10.6 of the Explanatory Supplement.
    *
    * If the required data are not available, or if no algorithm is known for
    * the calculation of the apparent visual magnitude of the specified object,
    * this member function returns an undefined Optional object.
    *
    * An undefined object is also returned when the phase angle of the object
    * at the time of calculation is beyond the limits of the set of
    * observations used to generate the underlying models. For Mercury,
    * apparent magnitudes are only calculated for phase angles
    * 2&deg; &le; \e i &le; 170&deg;. For Venus, the magnitude is only
    * calculated for 0&deg; < \e i &le; 163&deg;.7. The valid range for Mars is
    * \e i &le; 50&deg;.
    *
    * \sa CanComputeApparentVisualMagnitude()
    */
   Optional<double> ApparentVisualMagnitude( EphemerisFile::Handle& H );

   /*!
    * Conversion from rectangular equatorial to rectangular ecliptic
    * coordinates.
    *
    * \param q    Rectangular equatorial coordinates.
    *
    * \param se   Sine of the obliquity of the ecliptic.
    *
    * \param ce   Cosine of the obliquity of the ecliptic.
    *
    * Returns a vector whose components are the rectangular ecliptic
    * coordinates corresponding to the specified equatorial position \a q at
    * the epoch where the specified obliquity has been calculated.
    */
   static Vector EquatorialToEcliptic( const Vector& q, double se, double ce )
   {
      // Rx(eps)*q
      return Vector( q[0], q[1]*ce + q[2]*se, q[2]*ce - q[1]*se );
   }

   /*!
    * Conversion from rectangular equatorial to rectangular ecliptic
    * coordinates.
    *
    * \param q    Rectangular equatorial coordinates.
    *
    * \param eps  Oliquity of the ecliptic in radians.
    *
    * Returns a vector whose components are the rectangular ecliptic
    * coordinates corresponding to the specified equatorial position \a q at
    * the epoch where the specified obliquity has been calculated.
    */
   static Vector EquatorialToEcliptic( const Vector& q, double eps )
   {
      double se, ce; SinCos( eps, se, ce );
      return EquatorialToEcliptic( q, se, ce );
   }

   /*!
    * Conversion from spherical equatorial to spherical ecliptic coordinates.
    *
    * \param q    Spherical equatorial coordinates in radians, where \a q.x is
    *             the right ascension and \a q.y is the declination.
    *
    * \param se   Sine of the obliquity of the ecliptic.
    *
    * \param ce   Cosine of the obliquity of the ecliptic.
    *
    * Returns the ecliptic coordinates in radians as a point \e p, where \e p.x
    * is the longitude in the range [0,2pi) and \e p.y is the latitude in
    * [-pi/2,+pi/2].
    */
   static DPoint EquatorialToEcliptic( const DPoint& q, double se, double ce )
   {
      DPoint e;
      EquatorialToEcliptic( Vector::FromSpherical( q.x, q.y ), se, ce ).ToSpherical2Pi( e.x, e.y );
      return e;
   }

   /*!
    * Conversion from spherical equatorial to spherical ecliptic coordinates.
    *
    * \param q    Spherical equatorial coordinates in radians, where \a q.x is
    *             the right ascension and \a q.y is the declination.
    *
    * \param eps  Oliquity of the ecliptic in radians.
    *
    * Returns the ecliptic coordinates in radians as a point \e p, where \e p.x
    * is the longitude in the range [0,2pi) and \e p.y is the latitude in
    * [-pi/2,+pi/2].
    */
   static DPoint EquatorialToEcliptic( const DPoint& q, double eps )
   {
      double se, ce; SinCos( eps, se, ce );
      return EquatorialToEcliptic( q, se, ce );
   }

   /*!
    * Conversion from rectangular ecliptic to rectangular equatorial
    * coordinates.
    *
    * \param q    Rectangular ecliptic coordinates.
    *
    * \param se   Sine of the obliquity of the ecliptic.
    *
    * \param ce   Cosine of the obliquity of the ecliptic.
    *
    * Returns a vector whose components are the rectangular equatorial
    * coordinates corresponding to the specified ecliptic position \a q at
    * the epoch where the specified obliquity has been calculated.
    */
   static Vector EclipticToEquatorial( const Vector& q, double se, double ce )
   {
      // Rx(eps)*q
      return Vector( q[0], q[1]*ce - q[2]*se, q[2]*ce + q[1]*se );
   }

   /*!
    * Conversion from rectangular ecliptic to rectangular equatorial
    * coordinates.
    *
    * \param q    Rectangular ecliptic coordinates.
    *
    * \param eps  Oliquity of the ecliptic in radians.
    *
    * Returns a vector whose components are the rectangular equatorial
    * coordinates corresponding to the specified ecliptic position \a q at
    * the epoch where the specified obliquity has been calculated.
    */
   static Vector EclipticToEquatorial( const Vector& q, double eps )
   {
      double se, ce; SinCos( eps, se, ce );
      return EclipticToEquatorial( q, se, ce );
   }

   /*!
    * Conversion from spherical ecliptic to spherical equatorial coordinates.
    *
    * \param q    Spherical ecliptic coordinates in radians, where \a q.x is
    *             the longitude and \a q.y is the latitude.
    *
    * \param se   Sine of the obliquity of the ecliptic.
    *
    * \param ce   Cosine of the obliquity of the ecliptic.
    *
    * Returns the equatorial coordinates in radians as a point \e p, where \e p.x
    * is the right ascension in the range [0,2pi) and \e p.y is the declination
    * in [-pi/2,+pi/2].
    */
   static DPoint EclipticToEquatorial( const DPoint& q, double se, double ce )
   {
      DPoint e;
      EclipticToEquatorial( Vector::FromSpherical( q.x, q.y ), se, ce ).ToSpherical2Pi( e.x, e.y );
      return e;
   }

   /*!
    * Conversion from spherical ecliptic to spherical equatorial coordinates.
    *
    * \param q    Spherical ecliptic coordinates in radians, where \a q.x is
    *             the longitude and \a q.y is the latitude.
    *
    * \param eps  Oliquity of the ecliptic in radians.
    *
    * Returns the equatorial coordinates in radians as a point \e p, where \e p.x
    * is the right ascension in the range [0,2pi) and \e p.y is the declination
    * in [-pi/2,+pi/2].
    */
   static DPoint EclipticToEquatorial( const DPoint& q, double eps )
   {
      double se, ce; SinCos( eps, se, ce );
      return EclipticToEquatorial( q, se, ce );
   }

   /*!
    * Conversion from ICRS rectangular equatorial to rectangular galactic
    * coordinates.
    *
    * \param q    Rectangular equatorial coordinates in the ICRS.
    *
    * Returns a vector whose components are the calculated rectangular galactic
    * coordinates.
    *
    * In this routine we adopt the proposed ICRS coordinates of the
    * galactic pole in:
    *
    * Jia-Cheng Liu, Zi Zhu, and Hong Zhang, <em>Reconsidering the galactic
    * coordinate system</em>, Astronomy & Astrophysics manuscript no. AA2010,
    * October 26, 2018.
    *
    * The applied conventional definitions are as follows. The ICRS equatorial
    * coordinates of the zero point of galactic coordinates are:
    *
    * &alpha; = 17<sup>h</sup>45<sup>m</sup>40<sup>s</sup>.0400
    * &delta; = &ndash;29&deg;00'28".138
    *
    * The equatorial coordinates of the galactic pole, coherent with the ICRS,
    * are:
    *
    * &alpha;<sup>p</sup> = 12<sup>h</sup>51<sup>m</sup>36<sup>s</sup>.7151981
    * &delta;<sup>p</sup> = +27&deg;06'11".193172
    *
    * Note that these definitions are not consistent with the conventional
    * values currently accepted by the IAU. The current (as of October 2018)
    * galactic coordinate system was defined by the IAU in 1959 in the FK4
    * B1950.0 reference system.
    */
   static Vector ICRSEquatorialToGalactic( const Vector& q )
   {
      return Matrix( +0.494055821648, -0.054657353964, -0.445679169947,
                     -0.872844082054, -0.484928636070, +0.746511167077,
                     -0.867710446378, -0.198779490637, +0.455593344276 )*q;
   }

   /*!
    * Conversion from ICRS spherical equatorial to spherical galactic
    * coordinates.
    *
    * \param q    Spherical ICRS equatorial coordinates in radians, where
    *             \a q.x is the right ascension and \a q.y is the declination.
    *
    * Returns the galactic coordinates in radians as a point \e p, where \e p.x
    * is the longitude in the range [0,2pi) and \e p.y is the latitude in
    * [-pi/2,+pi/2].
    *
    * See the documentation for ICRSEquatorialToGalactic( const Vector& ) for
    * critical information on the adopted galactic coordinate system.
    */
   static DPoint ICRSEquatorialToGalactic( const DPoint& q )
   {
      DPoint g;
      ICRSEquatorialToGalactic( Vector::FromSpherical( q.x, q.y ) ).ToSpherical2Pi( g.x, g.y );
      return g;
   }

private:

   // TDB
   TimePoint   m_t;
   // TT
   TimePoint   m_tt;
   // UT1
   TimePoint   m_ut1;
   // TT in Julian centuries since J2000.0.
   double      m_TT;
   // Barycentric ICRS position and velocity of the Earth.
   Vector      m_Eb, m_Edb;
   // Geocentric ICRS position and velocity of the observer, in km and km/day.
   Vector      m_G, m_Gd;
   // Barycentric ICRS position of the Sun.
   Vector      m_Sb;
   // Heliocentric position of the Earth.
   Vector      m_Eh;
   // Distance Earth-Sun.
   double      m_E;
   // Heliocentric position of the observer (au).
   Vector      m_Oh;
   // Distance observer-Sun (au).
   double      m_O;
   // Bias+precession angles.
   double      m_gamb, m_phib, m_psib, m_epsa;
   // Nutation angles.
   double      m_dpsi, m_deps;
   // Position of the Celestial Intermediate Pole (CIP) in GCRS.
   double      m_X, m_Y;
   // CIO locator.
   double      m_s;
   // Combined bias-precession-nutation matrix (NPB).
   Matrix      m_M, m_Minv;
   // Combined bias-precession-nutation matrix (NPB_CIO).
   Matrix      m_C, m_Cinv;
   // Equation of the origins.
   double      m_EO;
   // Earth rotation angle.
   double      m_ERA;
   // Greenwich apparent sidereal time.
   double      m_GAST;
   // Light-travel time in days.
   double      m_tau = 0;
   // Barycentric position.
   Vector      m_ub;
   // True geocentric position.
   Vector      m_U0;
   // Geocentric position.
   Vector      m_U;
   // Astrometric place.
   Vector      m_u1;
   // Proper place.
   Vector      m_u2;
   // Apparent place.
   Vector      m_u3e;
   // Intermediate place.
   Vector      m_u3i;

   // Handles for calculation of fundamental ephemerides and nutation angles.
   AutoPointerCloner<EphemerisFile::Handle> m_TT_TDB, m_HE, m_HS, m_HN;

   // Current observer for calculation of topocentric coordinates.
   AutoPointerCloner<ObserverPosition> m_observer;

   // Special case flags.
   bool m_isMoon = false, m_isSun = false, m_isStar = false;

   // Whether to account for polar motion (CIP->ITRS) in calculation of
   // topocentric positions.
   bool m_usePolarMotion = true;

   // Unique identifier of the object whose positions are being calculated.
   uint64 m_uniqueObjectId = 0;

   template <class T>
   bool Validate( const T& obj )
   {
      if ( obj.m_uniqueId != m_uniqueObjectId )
      {
         m_U0 = m_U = m_ub = m_u1 = m_u2 = m_u3e = m_u3i = Vector();
         m_tau = 0;
         m_isMoon = m_isSun = m_isStar = false;
         m_uniqueObjectId = obj.m_uniqueId;
         return false;
      }
      return true;
   }

   Vector Deflection();
   Vector Aberration();

   static double CIOLocator( double T, double X, double Y );

   /*
    * Astronomical constants, IAU 2009/2012 and IERS 2003/2010.
    */
   constexpr static double au_km    = 149597870.7;          // astronomical unit (km)
   constexpr static double c_km_s   = 299792.458;           // speed of light (km/s)
   constexpr static double c_km_day = c_km_s*86400;         // speed of light (km/day)
   constexpr static double c_au_day = (c_km_s/au_km)*86400; // speed of light (au/day)
   constexpr static double earth_omega = 7.292115e-5;       // angular velocity of Earth in radians/s
};

// ----------------------------------------------------------------------------

} // pcl

#endif  // __PCL_Position_h

// ----------------------------------------------------------------------------
// EOF pcl/Position.h - Released 2022-03-12T18:59:29Z
