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