1 /*****************************************************************************
2  *   Copyright (C) 2004-2018 The pykep development team,                     *
3  *   Advanced Concepts Team (ACT), European Space Agency (ESA)               *
4  *                                                                           *
5  *   https://gitter.im/esa/pykep                                             *
6  *   https://github.com/esa/pykep                                            *
7  *                                                                           *
8  *   act@esa.int                                                             *
9  *                                                                           *
10  *   This program is free software; you can redistribute it and/or modify    *
11  *   it under the terms of the GNU General Public License as published by    *
12  *   the Free Software Foundation; either version 2 of the License, or       *
13  *   (at your option) any later version.                                     *
14  *                                                                           *
15  *   This program is distributed in the hope that it will be useful,         *
16  *   but WITHOUT ANY WARRANTY; without even the implied warranty of          *
17  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the           *
18  *   GNU General Public License for more details.                            *
19  *                                                                           *
20  *   You should have received a copy of the GNU General Public License       *
21  *   along with this program; if not, write to the                           *
22  *   Free Software Foundation, Inc.,                                         *
23  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.               *
24  *****************************************************************************/
25 
26 #include <boost/date_time/gregorian/gregorian.hpp>
27 #include <iomanip>
28 #include <cmath>
29 #include <iostream>
30 
31 #include <keplerian_toolbox/astro_constants.hpp>
32 #include <keplerian_toolbox/core_functions/convert_dates.hpp>
33 #include <keplerian_toolbox/epoch.hpp>
34 
35 namespace kep_toolbox
36 {
37 using namespace boost::gregorian;
38 using namespace boost::posix_time;
39 
40 /// Constructor.
41 /**
42 * Constructs an epoch from a non-gregorian date.
43 * \param[in] epoch_in A double indicating the non-gregorian date
44 * \param[in] epoch_type One of [epoch::MJD2000, epoch::MJD, epoch::JD]
45 */
epoch(const double & epoch_in,type epoch_type)46 epoch::epoch(const double &epoch_in, type epoch_type) : mjd2000_m(epoch_in)
47 {
48     switch (epoch_type) {
49         case MJD2000:
50             break;
51         case MJD:
52             mjd2000_m = mjd2mjd2000(epoch_in);
53             break;
54         case JD:
55             mjd2000_m = jd2mjd2000(epoch_in);
56             break;
57     }
58 }
59 
60 /// Constructor.
61 /**
62 * Constructs an epoch from a gregorian date. The time of the day is assumed to be midnight.
63 * \param[in] year The gregorian year
64 * \param[in] month The month of the year
65 * \param[in] day The day of the month
66 */
epoch(const greg_year & year,const greg_month & month,const greg_day & day)67 epoch::epoch(const greg_year &year, const greg_month &month, const greg_day &day)
68 {
69     set_posix_time(ptime(date(year, month, day)));
70 }
71 
72 /// Constructor.
73 /**
74 * Constructs an epoch from a boost ptime object (posix time)
75 * \param[in] posix_time The posix_time
76 */
epoch(const boost::posix_time::ptime & posix_time)77 epoch::epoch(const boost::posix_time::ptime &posix_time)
78 {
79     time_duration dt = posix_time - ptime(date(2000, 1, 1));
80     bool flag = false;
81     if (dt.is_negative()) {
82         flag = true;
83         dt = dt.invert_sign();
84     }
85     double fr_secs = static_cast<double>(dt.fractional_seconds()) * BOOST_DATE_PRECISION;
86     mjd2000_m = static_cast<double>(dt.hours()) / 24.0 + static_cast<double>(dt.minutes()) / 1440.0 + (static_cast<double>(dt.seconds()) + fr_secs) / 86400.0;
87     if (flag) mjd2000_m = -mjd2000_m;
88 }
89 
90 /// jd getter.
91 /**
92 * Returns the julian date
93 *
94 * @return double containing the julian date
95 *
96 */
jd() const97 double epoch::jd() const
98 {
99     return mjd20002jd(mjd2000_m);
100 }
101 
102 /// mjd getter.
103 /**
104 * Returns the modified julian date
105 *
106 * @return double containing the modified julian date
107 *
108 */
mjd() const109 double epoch::mjd() const
110 {
111     return mjd20002mjd(mjd2000_m);
112 }
113 
114 /// mjd2000 getter.
115 /**
116 * Gets the modified julian date 2000
117 * @return const reference to mjd2000
118 */
mjd2000() const119 double epoch::mjd2000() const
120 {
121     return mjd2000_m;
122 }
123 
124 /// Extracts the posix time
125 /**
126 * Returns the posix_time representation of the epoch. The method evaluates
127 * from the mjd2000 the number of days, months, seconds and
128 * micro/nano seconds passed since the 1st of January 2000 and uses this information
129 * to build the posix time
130 *
131 * @return ptime containing the posix time
132 *
133 */
get_posix_time() const134 ptime epoch::get_posix_time() const
135 {
136     long hrs, min, sec, fsec;
137     bool flag = false;
138     double copy = mjd2000_m;
139     if (copy < 0) {
140         copy = -copy;
141         flag = true;
142     }
143     hrs = static_cast<long>(copy * 24);
144     min = static_cast<long>((copy * 24 - static_cast<double>(hrs)) * 60);
145     sec = static_cast<long>((((copy * 24 - static_cast<double>(hrs)) * 60) - static_cast<double>(min)) * 60);
146     double dblfsec = ((((copy * 24 - static_cast<double>(hrs)) * 60) - static_cast<double>(min)) * 60) - static_cast<double>(sec);
147     std::ostringstream fsecstr;
148     fsecstr << std::setiosflags(std::ios::fixed) << std::setprecision(-std::log10(BOOST_DATE_PRECISION)) << dblfsec;
149     fsec = boost::lexical_cast<long>(fsecstr.str().substr(2, -std::log10(BOOST_DATE_PRECISION) + 1));
150     ptime retval;
151     if (flag)
152         retval = ptime(date(2000, 1, 1), time_duration(-hrs, -min, -sec, -fsec));
153     else
154         retval = ptime(date(2000, 1, 1), time_duration(hrs, min, sec, fsec));
155     return retval;
156 }
157 
158 /// Sets the epoch from a posix time
159 /**
160 * Sets the epoch to a particular posix_time.
161 *
162 * \param[in] posix_time containing the posix time
163 *
164 */
set_posix_time(const boost::posix_time::ptime & posix_time)165 void epoch::set_posix_time(const boost::posix_time::ptime &posix_time)
166 {
167 
168     mjd2000_m = epoch(posix_time).mjd2000();
169 }
170 
171 /// Returns an epoch constructed from a delimited string containing a date
172 /**
173  *  Builds an epoch from a delimited string. Excess digits in fractional seconds will be dropped. Ex:
174  * "1:02:03.123456999" => "1:02:03.123456".
175  *  This behavior depends on the precision defined in astro_constant.h used to compile
176  *
177  * Example:
178  * 	std::string ts("2002-01-20 23:59:54.003");
179  * 	epoch e(epoch_from_string(ts))
180  *
181  */
epoch_from_string(const std::string date)182 epoch epoch_from_string(const std::string date)
183 {
184     return epoch(boost::posix_time::ptime(boost::posix_time::time_from_string(date)));
185 }
186 
187 /// Returns an epoch constructed from a non delimited iso string containing a date
188 /**
189  *  Builds an epoch from a non delimited iso string containing a date.
190  *
191  * Example:
192  * 	std::string ts("20020131T235959");
193  * 	epoch e(epoch_from_iso_string(ts))
194  *
195  */
epoch_from_iso_string(const std::string date)196 epoch epoch_from_iso_string(const std::string date)
197 {
198     return epoch(boost::posix_time::ptime(boost::posix_time::from_iso_string(date)));
199 }
200 
201 } // end of namespace kep_toolbox
202 
203 /// Overload the stream operator for kep_toolbox::epoch
204 /**
205  * Streams out a date in the format 2000-Jan-01 00:12:30.123457
206  *
207  * \param[in] s stream to which the epoch will be sent
208  * \param[in] now epoch to be sent to stream
209  *
210  * \return reference to s
211  *
212  */
operator <<(std::ostream & s,const kep_toolbox::epoch & now)213 std::ostream &kep_toolbox::operator<<(std::ostream &s, const kep_toolbox::epoch &now)
214 {
215     s << now.get_posix_time();
216     return s;
217 }
218