GammaLib  2.1.0.dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
GTime.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  * GTime.cpp - Time class *
3  * ----------------------------------------------------------------------- *
4  * copyright (C) 2010-2021 by Juergen Knoedlseder *
5  * ----------------------------------------------------------------------- *
6  * *
7  * This program is free software: you can redistribute it and/or modify *
8  * it under the terms of the GNU General Public License as published by *
9  * the Free Software Foundation, either version 3 of the License, or *
10  * (at your option) any later version. *
11  * *
12  * This program is distributed in the hope that it will be useful, *
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15  * GNU General Public License for more details. *
16  * *
17  * You should have received a copy of the GNU General Public License *
18  * along with this program. If not, see <http://www.gnu.org/licenses/>. *
19  * *
20  ***************************************************************************/
21 /**
22  * @file GTime.cpp
23  * @brief Time class implementation
24  * @author Juergen Knoedlseder
25  */
26 
27 /* __ Includes ___________________________________________________________ */
28 #ifdef HAVE_CONFIG_H
29 #include <config.h>
30 #endif
31 #include <ctime>
32 #include <cstring> // std::memcpy
33 #include <cstdio>
34 #include "GTools.hpp"
35 #include "GMath.hpp"
36 #include "GException.hpp"
37 #include "GTime.hpp"
38 #include "GTimeReference.hpp"
39 
40 /* __ Constants __________________________________________________________ */
41 const double mjd_ref = 55197.000766018518519; //!< MJD of time=0
42 const double jd_ref = mjd_ref + 2400000.5; //!< JD of time=0
43 
44 /* __ Method name definitions ____________________________________________ */
45 #define G_CONSTRUCT "GTime::GTime(double&, std::string&)"
46 #define G_SECS_GET "GTime::secs(std::string&)"
47 #define G_SECS_SET "GTime::secs(double&, std::string&)"
48 #define G_UTC "GTime::utc(std::string&)"
49 #define G_UTC_GET "GTime::utc(int&)"
50 
51 /* __ Macros _____________________________________________________________ */
52 
53 /* __ Coding definitions _________________________________________________ */
54 
55 /* __ Debug definitions __________________________________________________ */
56 
57 
58 /*==========================================================================
59  = =
60  = Constructors/destructors =
61  = =
62  ==========================================================================*/
63 
64 /***********************************************************************//**
65  * @brief Void constructor
66  ***************************************************************************/
68 {
69  // Initialise private members
70  init_members();
71 
72  // Return
73  return;
74 }
75 
76 
77 /***********************************************************************//**
78  * @brief Copy constructor
79  *
80  * @param[in] time Time.
81  ***************************************************************************/
82 GTime::GTime(const GTime& time)
83 {
84  // Initialise private members
85  init_members();
86 
87  // Copy members
88  copy_members(time);
89 
90  // Return
91  return;
92 }
93 
94 
95 /***********************************************************************//**
96  * @brief Time constructor
97  *
98  * @param[in] time Time value in TT (seconds or days).
99  * @param[in] unit Time unit string.
100  *
101  * @exception GException::invalid_argument
102  * Invalid time unit specified.
103  *
104  * Constructs a GTime object by setting the time in the native reference
105  * in the TT time system in units of seconds (default) or days.
106  ***************************************************************************/
107 GTime::GTime(const double& time, const std::string& unit)
108 {
109  // Initialise private members
110  init_members();
111 
112  // Set time according to timeunit string
113  std::string timeunit = gammalib::tolower(unit);
114  if (timeunit == "d" || timeunit == "day" || timeunit == "days") {
115  days(time);
116  }
117  else if (timeunit == "s" || timeunit == "sec" || timeunit == "secs") {
118  secs(time);
119  }
120  else {
121  std::string msg = "Invalid time unit \""+unit+"\" specified. Please "
122  "specify one of \"d\", \"day\", \"days\", \"s\", "
123  "\"sec\" or \"secs\"";
125  }
126 
127  // Return
128  return;
129 }
130 
131 
132 /***********************************************************************//**
133  * @brief Time constructor
134  *
135  * @param[in] time Time in given reference system.
136  * @param[in] ref Reference system.
137  *
138  * Constructs a GTime object by setting the time to a value given in a
139  * specific reference system.
140  ***************************************************************************/
141 GTime::GTime(const double& time, const GTimeReference& ref)
142 {
143  // Initialise private members
144  init_members();
145 
146  // Set time
147  set(time, ref);
148 
149  // Return
150  return;
151 }
152 
153 
154 /***********************************************************************//**
155  * @brief Time constructor
156  *
157  * @param[in] time Time string in given reference system.
158  * @param[in] ref Reference system.
159  *
160  * Constructs a GTime object by setting the time to a string value. See the
161  * set(std::string&, GTimeReference&) method for valid time strings.
162  ***************************************************************************/
163 GTime::GTime(const std::string& time, const GTimeReference& ref)
164 {
165  // Initialise private members
166  init_members();
167 
168  // Set time
169  set(time, ref);
170 
171  // Return
172  return;
173 }
174 
175 
176 /***********************************************************************//**
177  * @brief Time constructor
178  *
179  * @param[in] time Time string.
180  *
181  * Constructs a GTime object by setting the time to a string value. See the
182  * set(const std::string& time) method for valid time strings.
183  ***************************************************************************/
184 GTime::GTime(const std::string& time)
185 {
186  // Initialise private members
187  init_members();
188 
189  // Set time
190  set(time);
191 
192  // Return
193  return;
194 }
195 
196 
197 /***********************************************************************//**
198  * @brief Destructor
199  ***************************************************************************/
201 {
202  // Free members
203  free_members();
204 
205  // Return
206  return;
207 }
208 
209 
210 /*==========================================================================
211  = =
212  = Operators =
213  = =
214  ==========================================================================*/
215 
216 /***********************************************************************//**
217  * @brief Assignment operator
218  *
219  * @param[in] time Time.
220  * @return Time.
221  ***************************************************************************/
223 {
224  // Execute only if object is not identical
225  if (this != &time) {
226 
227  // Free members
228  free_members();
229 
230  // Initialise private members for clean destruction
231  init_members();
232 
233  // Copy members
234  copy_members(time);
235 
236  } // endif: object was not identical
237 
238  // Return
239  return *this;
240 }
241 
242 
243 /*==========================================================================
244  = =
245  = Public methods =
246  = =
247  ==========================================================================*/
248 
249 /***********************************************************************//**
250  * @brief Clear time
251  ***************************************************************************/
252 void GTime::clear(void)
253 {
254  // Free members
255  free_members();
256 
257  // Initialise members
258  init_members();
259 
260  // Return
261  return;
262 }
263 
264 
265 /***********************************************************************//**
266  * @brief Clone time
267  *
268  * @return Pointer to deep copy of time.
269  ***************************************************************************/
270 GTime* GTime::clone(void) const
271 {
272  // Clone this image
273  return new GTime(*this);
274 }
275 
276 
277 /***********************************************************************//**
278  * @brief Return time in Julian Days (TT)
279  *
280  * @return Time in Julian Days (TT) (days).
281  *
282  * Returns the time in Julian Days (JD) in the Terrestrial Time (TT) system.
283  ***************************************************************************/
284 double GTime::jd(void) const
285 {
286  // Convert time from MET to JD
287  double jd = m_time * gammalib::sec2day + jd_ref;
288 
289  // Return Julian Days
290  return jd;
291 }
292 
293 
294 /***********************************************************************//**
295  * @brief Return time in Julian Days for time system
296  *
297  * @param[in] timesys Time system (one of "TT", "TAI", "UTC")
298  * @return Time in Julian Days (days).
299  *
300  * Returns the time in Julian Days (JD) for the specified time system.
301  ***************************************************************************/
302 double GTime::jd(const std::string& timesys) const
303 {
304  // Convert time to Julian Days
305  double jd = secs(timesys) * gammalib::sec2day + jd_ref;
306 
307  // Return Julian Days
308  return jd;
309 }
310 
311 
312 /***********************************************************************//**
313  * @brief Return time in Modified Julian Days (TT)
314  *
315  * @return Time in Modified Julian Days (TT) (days).
316  *
317  * Returns the time in Modified Julian Days (MJD) in the Terrestrial Time
318  * (TT) system.
319  ***************************************************************************/
320 double GTime::mjd(void) const
321 {
322  // Convert time to Modified Julian Days
323  double mjd = m_time * gammalib::sec2day + mjd_ref;
324 
325  // Return Modified Julian Days
326  return mjd;
327 }
328 
329 
330 /***********************************************************************//**
331  * @brief Return time in Modified Julian Days for time system
332  *
333  * @param[in] timesys Time system (one of "TT", "TAI", "UTC")
334  * @return Time in Modified Julian Days (days).
335  *
336  * Returns the time in Modified Julian Days (JD) for the specified time
337  * system.
338  ***************************************************************************/
339 double GTime::mjd(const std::string& timesys) const
340 {
341  // Convert time to Modified Julian Days
342  double mjd = secs(timesys) * gammalib::sec2day + mjd_ref;
343 
344  // Return Modified Julian Days
345  return mjd;
346 }
347 
348 
349 /***********************************************************************//**
350  * @brief Return time in seconds in native reference for time system
351  *
352  * @param[in] timesys Time system (one of "TT", "TAI", "UTC")
353  * @return Time (seconds).
354  ***************************************************************************/
355 double GTime::secs(const std::string& timesys) const
356 {
357  // Initialise time
358  double time;
359 
360  // If system is TT then return the stored time ...
361  if (timesys == "TT") {
362  time = m_time;
363  }
364 
365  // ... otherwise if the system if TAI then subtract an offset ...
366  else if (timesys == "TAI") {
367  time = m_time - gammalib::tai2tt;
368  }
369 
370  // ... otherwise if the system is UTC then convert the time from TT to TAI
371  // and subtract the leap seconds. The repeated calling of the leap second
372  // method is a kluge to converge as the argument is given in the UTC system
373  // but upon start we're in the TAI system.
374  else if (timesys == "UTC") {
375  time = m_time - gammalib::tai2tt; // Time in TAI
376  double mjd = time * gammalib::sec2day + mjd_ref;
377  double leaps = leap_seconds(mjd);
378  leaps = leap_seconds(mjd - leaps * gammalib::sec2day);
379  leaps = leap_seconds(mjd - leaps * gammalib::sec2day);
380  time -= leaps;
381  }
382 
383  // ... otherwise throw an exception
384  else {
385  std::string msg = "Unknown time system \""+timesys+"\". Either specify "
386  "\"TT\", \"TAI\" or \"UTC\".";
388  }
389 
390  // Return time
391  return time;
392 }
393 
394 
395 /***********************************************************************//**
396  * @brief Return time in days in native reference (TT)
397  *
398  * @return Time in native reference (days).
399  ***************************************************************************/
400 double GTime::days(void) const
401 {
402  // Return time
403  return (m_time * gammalib::sec2day);
404 }
405 
406 
407 /***********************************************************************//**
408  * @brief Return time in days in native reference for time system
409  *
410  * @param[in] timesys Time system (one of "TT", "TAI", "UTC")
411  * @return Time in native reference (days).
412  ***************************************************************************/
413 double GTime::days(const std::string& timesys) const
414 {
415  // Return time
416  return (secs(timesys) * gammalib::sec2day);
417 }
418 
419 
420 /***********************************************************************//**
421  * @brief Return Julian epoch in native reference (TT)
422  *
423  * @return Julian epoch (years).
424  ***************************************************************************/
425 double GTime::julian_epoch(void) const
426 {
427  // Compute Julian epoch
428  double julian_epoch = 2000.0 + (jd() - 2451545.0) / 365.25;
429 
430  // Return Julian epoch
431  return (julian_epoch);
432 }
433 
434 
435 /***********************************************************************//**
436  * @brief Return Julian epoch in native reference for time system
437  *
438  * @param[in] timesys Time system (one of "TT", "TAI", "UTC")
439  * @return Julian epoch (years).
440  ***************************************************************************/
441 double GTime::julian_epoch(const std::string& timesys) const
442 {
443  // Compute Julian epoch
444  double julian_epoch = 2000.0 + (jd(timesys) - 2451545.0) / 365.25;
445 
446  // Return Julian epoch
447  return (julian_epoch);
448 }
449 
450 
451 /***********************************************************************//**
452  * @brief Return time as string in UTC time system
453  *
454  * @param[in] precision Digits of precision to show in the seconds field
455  * @return Time as string in UTC time system.
456  *
457  * Returns time in the format YYYY-MM-DDThh:mm:ss(.ss...), where YYYY is a
458  * four-digit year, MM a two-digit month, DD a two-digit day of month, hh two
459  * digits of hour (0 through 23), mm two digits of minutes, and ss(.ss...)
460  * two digits of second (ISO 8601 time standard). In case that
461  * @p precision > 0 digits in the second after the comma will be returned.
462  *
463  * The method is only valid for dates from year 1972 on.
464  ***************************************************************************/
465 std::string GTime::utc(const int& precision) const
466 {
467  // Check argument
468  if (precision < 0) {
469  std::string msg = "Invalid precision \""+gammalib::str(precision)+"\""
470  " encountered. Please specify a precision >= 0.";
472  }
473 
474  // Define number of days per month
475  static int daymonth[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
476 
477  // Get MJD in TAI time system
478  double mjd = this->mjd() - gammalib::tai2tt * gammalib::sec2day;
479 
480  // Correct for leap seconds (repeat is a kluge to converge as the
481  // argument is given in the UTC system but upon start we're in the TAI
482  // system
483  double leaps = leap_seconds(mjd) * gammalib::sec2day;
484  leaps = leap_seconds(mjd - leaps) * gammalib::sec2day;
485  leaps = leap_seconds(mjd - leaps) * gammalib::sec2day;
486  mjd -= leaps;
487 
488  // Split in day and fraction
489  int day = (int)mjd;
490  double fraction = mjd - (double)day;
491 
492  // Get margin for computation. We add the margin to the seconds and
493  // subtract it later to avoid rounding to 60 seconds
494  double margin = 0.5;
495  if (precision > 0) {
496  switch (precision) {
497  case 1:
498  margin = 0.05;
499  break;
500  case 2:
501  margin = 0.005;
502  break;
503  case 3:
504  margin = 0.0005;
505  break;
506  default:
507  for (int i = 0; i < precision; ++i) {
508  margin *= 0.1;
509  }
510  break;
511  }
512  }
513 
514  // Compute time in day
515  double second = fraction * gammalib::sec_in_day + margin;
516  int hour = (int)second / 3600;
517  second -= hour * 3600.0;
518  int minute = (int)second / 60;
519  second -= minute * 60.0;
520  if (hour > 23) {
521  hour -= 24;
522  day++;
523  }
524  second -= margin;
525  if (second < 0.0) {
526  second = 0.0;
527  }
528 
529  // Compute year and day in the year
530  int year = 1972; // Set year to 1972
531  day -= 41317; // Subtract MJD of 1-1-1972
532  day++; // Day 0 is 1 January
533  int days = days_in_year(year);
534  while (day > days) {
535  day -= days;
536  year++;
537  days = days_in_year(year);
538  }
539  while (day < 0) {
540  year--;
541  days = days_in_year(year);
542  day += days;
543  }
544 
545  // Adjust number of days per month for leap years
546  if (is_leap_year(year)) {
547  daymonth[1] = 29;
548  }
549  else {
550  daymonth[1] = 28;
551  }
552 
553  // Compute month and day in the month
554  int month = 0;
555  while (month < 12) {
556  if (day <= daymonth[month]) {
557  break;
558  }
559  day -= daymonth[month];
560  month++;
561  }
562  month++;
563 
564  // Calculate the width of the seconds' pattern, plus two for the integer
565  // seconds, plus one if a decimal is needed
566  int sec_width = precision + 2;
567  if (precision > 0) {
568  sec_width += 1;
569  }
570 
571  // Format pattern for variable seconds precision
572  char utc_pattern[50];
573  sprintf(utc_pattern, "%%4.4d-%%2.2d-%%2.2dT%%2.2d:%%2.2d:%%0%d.0%df",
574  sec_width, precision);
575 
576  // Create string
577  char utc[32];
578  sprintf(utc, utc_pattern,
579  year, month, day, hour, minute, second);
580 
581  // Return
582  return (std::string(utc));
583 }
584 
585 
586 /***********************************************************************//**
587  * @brief Return Greenwich mean sidereal time in hours in a day
588  *
589  * @return Greenwich mean sidereal time (hours).
590  *
591  * See http://aa.usno.navy.mil/faq/docs/GAST.php
592  ***************************************************************************/
593 double GTime::gmst(void) const
594 {
595  // Get days since 1 January 2000, 12h in Universal Time
596  double d = days("UTC") + 3652.50076602;
597 
598  // Compute Greenwich mean sidereal time in hours
599  double gmst = 18.697374558 + 24.06570982441908 * d;
600 
601  // Put into [0,24]
602  gmst -= floor(gmst/24.0) * 24.0;
603 
604  // Return Greenwich mean sidereal time in hours
605  return gmst;
606 }
607 
608 
609 /***********************************************************************//**
610  * @brief Return Greenwich apparent sidereal time in hours in a day
611  *
612  * @return Greenwich apparent sidereal time (hours).
613  *
614  * See http://aa.usno.navy.mil/faq/docs/GAST.php
615  ***************************************************************************/
616 double GTime::gast(void) const
617 {
618  // Get days since 1 January 2000, 12h in Universal Time
619  double d = days("UTC") + 3652.50076602;
620 
621  // Compute longitude of the ascending node of the Moon in degrees
622  double Omega = 125.04 - 0.052954 * d;
623 
624  // Compute mean longitude of the Sun in degrees
625  double L = 280.47 + 0.98565 * d;
626 
627  // Compute nutation in longitude in hours
628  double DeltaPsi = -0.000319 * std::sin(Omega * gammalib::deg2rad) -
629  0.000024 * std::sin(2.0 * L * gammalib::deg2rad);
630 
631  // Compute the obliquity in degrees
632  double epsilon = 23.4393 - 0.0000004 * d;
633 
634  // Compute equation of equinoxes
635  double eqeq = DeltaPsi * std::cos(epsilon * gammalib::deg2rad);
636 
637  // Compute Greenwich apparent sidereal time in hours
638  double gast = gmst() + eqeq;
639 
640  // Put into [0,24]
641  gast -= floor(gast/24.0) * 24.0;
642 
643  // Return Greenwich apparent sidereal time in hours
644  return gast;
645 }
646 
647 
648 /***********************************************************************//**
649  * @brief Return local mean sidereal time in hours in a day
650  *
651  * @param[in] geolon Geographic longitude West of Greenwich (degrees).
652  * @return Local mean sidereal time (hours).
653  *
654  * See http://aa.usno.navy.mil/faq/docs/GAST.php
655  ***************************************************************************/
656 double GTime::lmst(const double& geolon) const
657 {
658  // Compute local mean siderial time
659  double lmst = gmst() - geolon/15.0;
660 
661  // Put into [0,24]
662  lmst -= floor(lmst/24.0) * 24.0;
663 
664  // Return local mean sidereal time in hours
665  return lmst;
666 }
667 
668 
669 /***********************************************************************//**
670  * @brief Return local apparent sidereal time in hours in a day
671  *
672  * @param[in] geolon Geographic longitude West of Greenwich (degrees).
673  * @return Local apparent sidereal time (hours).
674  *
675  * See http://aa.usno.navy.mil/faq/docs/GAST.php
676  ***************************************************************************/
677 double GTime::last(const double& geolon) const
678 {
679  // Compute local apparent siderial time
680  double last = gast() - geolon/15.0;
681 
682  // Put into [0,24]
683  last -= floor(last/24.0) * 24.0;
684 
685  // Return local mean sidereal time in hours
686  return last;
687 }
688 
689 
690 /***********************************************************************//**
691  * @brief Return time in specified reference
692  *
693  * @return Time in specified reference.
694  *
695  * Convert the time from the native reference system into the specified
696  * reference system.
697  ***************************************************************************/
698 double GTime::convert(const GTimeReference& ref) const
699 {
700  // Retrieve time in native reference (TT in seconds)
701  double time = m_time;
702 
703  // Compute time offset in seconds
704  double offset = (mjd_ref - ref.mjdref()) * gammalib::sec_in_day;
705 
706  // Add time offset in seconds
707  time += offset;
708 
709  // Subtract leap seconds in case that time is requested in UTC time
710  // system
711  if (gammalib::toupper(ref.timesys()) == "UTC") {
712 
713  // Get MJD in TAI time system
714  double mjd = this->mjd() - gammalib::tai2tt * gammalib::sec2day;
715 
716  // Get leap seconds (repeat is a kluge to converge as the
717  // argument is given in the UTC system but upon start we're in the TAI
718  // system
719  double leaps = leap_seconds(mjd);
720  leaps = leap_seconds(mjd - leaps * gammalib::sec2day);
721  leaps = leap_seconds(mjd - leaps * gammalib::sec2day);
722 
723  // Subtract leap seconds and TAI offset
724  time -= (leaps + gammalib::tai2tt);
725 
726  } // endif: UTC time system has been requested
727 
728  // ... otherwise, if time is requested in TAI system then subtract the
729  // TAI offset
730  else if (gammalib::toupper(ref.timesys()) == "TAI") {
731  time -= gammalib::tai2tt;
732  }
733 
734  // Convert to specified time unit
735  double to_unit = ref.unitseconds();
736  if (to_unit != 1.0) {
737  time /= to_unit;
738  }
739 
740  // Return time
741  return time;
742 }
743 
744 
745 /***********************************************************************//**
746  * @brief Set time in Julian Days in native reference (TT)
747  *
748  * @param[in] time Time in Julian Days (TT) (days).
749  ***************************************************************************/
750 void GTime::jd(const double& time)
751 {
752  // Convert time from Julian Days to seconds in native reference
753  m_time = (time - jd_ref) * gammalib::sec_in_day;
754 
755  // Return
756  return;
757 }
758 
759 
760 /***********************************************************************//**
761  * @brief Set time in Julian Days in native reference for time system
762  *
763  * @param[in] time Time in Julian Days (days).
764  * @param[in] timesys Time system (one of "TT", "TAI", "UTC")
765  ***************************************************************************/
766 void GTime::jd(const double& time, const std::string& timesys)
767 {
768  // Convert time from Julian Days to seconds in native reference
769  double seconds = (time - jd_ref) * gammalib::sec_in_day;
770 
771  // Set time according to the specified time system
772  secs(seconds, timesys);
773 
774  // Return
775  return;
776 }
777 
778 
779 /***********************************************************************//**
780  * @brief Set time in Modified Julian Days in native reference (TT)
781  *
782  * @param[in] time Time in Modified Julian Days (TT) (days).
783  ***************************************************************************/
784 void GTime::mjd(const double& time)
785 {
786  // Convert time from Modified Julian Days to native (seconds)
787  m_time = (time - mjd_ref) * gammalib::sec_in_day;
788 
789  // Return
790  return;
791 }
792 
793 
794 /***********************************************************************//**
795  * @brief Set time in Modified Julian Days in native reference for time
796  * system
797  *
798  * @param[in] time Time in Modified Julian Days (days).
799  * @param[in] timesys Time system (one of "TT", "TAI", "UTC")
800  ***************************************************************************/
801 void GTime::mjd(const double& time, const std::string& timesys)
802 {
803  // Convert time from Modified Julian Days to seconds in native reference
804  double seconds = (time - mjd_ref) * gammalib::sec_in_day;
805 
806  // Set time according to the specified time system
807  secs(seconds, timesys);
808 
809  // Return
810  return;
811 }
812 
813 
814 /***********************************************************************//**
815  * @brief Set time in seconds in native reference for time system
816  *
817  * @param[in] seconds Time in native reference (seconds).
818  * @param[in] timesys Time system (one of "TT", "TAI", "UTC")
819  ***************************************************************************/
820 void GTime::secs(const double& seconds, const std::string& timesys)
821 {
822  // If system is TT then simply set time ...
823  if (timesys == "TT") {
824  m_time = seconds;
825  }
826 
827  // ... otherwise if the system if TAI then add an offset ...
828  else if (timesys == "TAI") {
829  m_time = seconds + gammalib::tai2tt;
830  }
831 
832  // ... otherwise if the system is UTC then convert the time from TT to TAI
833  // and add the leap seconds ...
834  else if (timesys == "UTC") {
835  double mjd = seconds * gammalib::sec2day + mjd_ref; // Time in UTC
836  double leaps = leap_seconds(mjd);
837  m_time = seconds + gammalib::tai2tt + leaps;
838  }
839 
840  // ... otherwise throw an exception
841  else {
842  std::string msg = "Unknown time system \""+timesys+"\". Either specify "
843  "\"TT\", \"TAI\" or \"UTC\".";
845  }
846 
847  // Return
848  return;
849 }
850 
851 
852 /***********************************************************************//**
853  * @brief Set time in days in native reference (TT)
854  *
855  * @param[in] days Time (TT) (days).
856  ***************************************************************************/
857 void GTime::days(const double& days)
858 {
859  // Set time
860  m_time = days * gammalib::sec_in_day;
861 
862  // Return
863  return;
864 }
865 
866 
867 /***********************************************************************//**
868  * @brief Set time in days in native reference for time system
869  *
870  * @param[in] days Time (TT) (days).
871  * @param[in] timesys Time system (one of "TT", "TAI", "UTC")
872  ***************************************************************************/
873 void GTime::days(const double& days, const std::string& timesys)
874 {
875  // Convert time from days to seconds in native reference
876  double seconds = days * gammalib::sec_in_day;
877 
878  // Set time according to the specified time system
879  secs(seconds, timesys);
880 
881  // Return
882  return;
883 }
884 
885 
886 /***********************************************************************//**
887  * @brief Set time as string in UTC time system
888  *
889  * @param[in] time Time string (UTC).
890  *
891  * @exception GException::invalid_argument
892  * Invalid time string specified.
893  *
894  * The time has to be given in the format YYYY-MM-DDThh:mm:ss.s, where
895  * YYYY is a four-digit year, MM a two-digit month, DD a two-digit day of
896  * month, hh two digits of hour (0 through 23), mm two digits of minutes,
897  * ss two digits of second and s one or more digits representing a
898  * decimal fraction of a second (ISO 8601 time standard).
899  *
900  * The method is only valid for dates from year 1972 on.
901  ***************************************************************************/
902 void GTime::utc(const std::string& time)
903 {
904  // Define number of days per month
905  static int daymonth[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
906 
907  // Analyse the UTC string
908  long year = 0;
909  int month = 0;
910  long day = 0;
911  long hour = 0;
912  long minute = 0;
913  double second = 0.0;
914  int n = sscanf(time.c_str(), "%ld-%d-%ldT%ld:%ld:%lg",
915  &year, &month, &day, &hour, &minute, &second);
916 
917  // Adjust number of days per month for leap years
918  if (is_leap_year(year)) {
919  daymonth[1] = 29;
920  }
921  else {
922  daymonth[1] = 28;
923  }
924 
925  // Check time string
926  if (n != 3 && n != 6) {
927  std::string msg = "Invalid time string \""+time+"\" encountered. "
928  "Please specify the time in the format YYYY-MM-DD "
929  "or YYYY-MM-DDThh:mm:ss.s.";
931  }
932  if (year < 1000 || year > 9999) {
933  std::string msg = "Invalid year "+gammalib::str(year)+" specified. "
934  "The year needs to be a four-digit year.";
936  }
937  if (month < 1 || month > 12) {
938  std::string msg = "Invalid month "+gammalib::str(month)+" specified. "
939  "The month needs to be comprised between 01 and 12";
941  }
942  if (day < 1 || day > daymonth[month-1]) {
943  std::string msg = "Invalid day "+gammalib::str(day)+" specified. "
944  "The day for month "+gammalib::str(month)+" needs "
945  "to be comprised between 01 and "+
946  gammalib::str(daymonth[month-1])+".";
948  }
949  if (hour < 0 || hour > 23) {
950  std::string msg = "Invalid hour "+gammalib::str(hour)+" specified. "
951  "The hour needs to be comprised between 00 and 23";
953  }
954  if (minute < 0 || minute > 59) {
955  std::string msg = "Invalid minute "+gammalib::str(minute)+" specified. "
956  "The minute needs to be comprised between 00 and 59";
958  }
959  if (second < 0 || second >= 60.0) {
960  std::string msg = "Invalid second "+gammalib::str(second)+" specified. "
961  "The second needs to be comprised between 00 and <60";
963  }
964 
965  // Compute MJD day
966  month--;
967  for (int i = 0; i < month; ++i) { // Add days passed per month
968  day += daymonth[i];
969  }
970  day += (year - 1972) * 365 - 1; // Add days passed per year
971  day += (year - 1969) / 4; // Add leap days passed (every 4 years)
972  day -= (year - 1901) / 100; // Add leap days passed (not every 100 years)
973  day += (year - 1601) / 400; // Add leap days passed (every 400 years)
974  day += 41317; // Add MJD at 1972
975 
976  // Compute MJD fraction (UTC)
977  double fraction = ((double)hour * 3600.0 + (double)minute * 60.0 + second) *
979 
980  // Compute MJD (UTC)
981  double mjd = (double)day + fraction;
982 
983  // Get conversion from UTC to TAI to TT
984  double correction = leap_seconds(mjd) + gammalib::tai2tt;
985 
986  // Convert MJD from UTC to TT
987  mjd += correction * gammalib::sec2day;
988 
989  // Set time
990  this->mjd(mjd);
991 
992  // Return
993  return;
994 }
995 
996 
997 /***********************************************************************//**
998  * @brief Set time given in specified reference
999  *
1000  * @param[in] time Time in given reference system.
1001  * @param[in] ref Reference system.
1002  *
1003  * Set the time to a value given in a specific reference system.
1004  ***************************************************************************/
1005 void GTime::set(const double& time, const GTimeReference& ref)
1006 {
1007  // Convert time to specified time unit
1008  m_time = time * ref.unitseconds();
1009 
1010  // Compute time offset in seconds
1011  double offset = (mjd_ref - ref.mjdref()) * gammalib::sec_in_day;
1012 
1013  // Subtract time offset in seconds
1014  m_time -= offset;
1015 
1016  // Add leap seconds in case that time was given in UTC time system
1017  if (gammalib::toupper(ref.timesys()) == "UTC") {
1018 
1019  // Add leap seconds and offset between TAI and TT time system
1020  m_time += leap_seconds(this->mjd());
1022 
1023  } // endif: Time was given in UTC time system
1024 
1025  // ... otherwise if system is TAI system then add TAI offset
1026  else if (gammalib::toupper(ref.timesys()) == "TAI") {
1028  }
1029 
1030  // Return
1031  return;
1032 }
1033 
1034 
1035 /***********************************************************************//**
1036  * @brief Set time from string
1037  *
1038  * @param[in] time Time string.
1039  * @param[in] ref Reference system.
1040  *
1041  * Sets the time from a string for a given reference system. The following
1042  * strings are valid:
1043  *
1044  * "2016-10-05T15:08:56" (UTC string)
1045  * "1800.0" (MET seconds in specified reference system)
1046  * "1800.0 (TT)" (MET seconds in specified reference, TT system)
1047  * "1800.0 (UTC)" (MET seconds in specified reference, UTC time system)
1048  * "1800.0 (TAI)" (MET seconds in specified reference, TAI time system)
1049  * "MJD 54609" (Modified Julian Days, TT system)
1050  * "MJD 54609 (TT)" (Modified Julian Days, TT system)
1051  * "MJD 54609 (UTC)" (Modified Julian Days, UTC system)
1052  * "MJD 54609 (TAI)" (Modified Julian Days, TAI system)
1053  * "JD 54609" (Julian Days, TT system)
1054  * "JD 54609 (TT)" (Julian Days, TT system)
1055  * "JD 54609 (UTC)" (Julian Days, UTC system)
1056  * "JD 54609 (TAI)" (Julian Days, TAI system)
1057  *
1058  * If any other string is encountered, the numerical value is interpreted as
1059  * time is seconds using the TT system.
1060  *
1061  * Note that the TT, UTC or TAI attributes overwrite the values contained in
1062  * the specified reference system. The reference system is only used to
1063  * convert MET times in seconds.
1064  ***************************************************************************/
1065 void GTime::set(const std::string& time, const GTimeReference& ref)
1066 {
1067  // Strip any whitespace from string and convert it to upper case
1068  std::string str = gammalib::toupper(gammalib::strip_whitespace(time));
1069 
1070  // First check if the string is a UTC string
1071  long year = 0;
1072  int month = 0;
1073  long day = 0;
1074  long hour = 0;
1075  long minute = 0;
1076  double second = 0.0;
1077  int n = sscanf(str.c_str(), "%ld-%d-%ldT%ld:%ld:%lg",
1078  &year, &month, &day, &hour, &minute, &second);
1079  if (n == 3 || n == 6) {
1080  utc(str);
1081  }
1082 
1083  // ... otherwise check for Modified Julian Days
1084  else if (str.find("MJD") == 0) {
1085  double timeval = extract_timeval(str);
1086  std::string timesys = extract_timesys(str);
1087  if (timesys.empty()) {
1088  timesys = "TT";
1089  }
1090  mjd(timeval, timesys);
1091  }
1092 
1093  // ... otherwise check for Julian Days
1094  else if (str.find("JD") == 0) {
1095  double timeval = extract_timeval(str);
1096  std::string timesys = extract_timesys(str);
1097  if (timesys.empty()) {
1098  timesys = "TT";
1099  }
1100  jd(timeval, timesys);
1101  }
1102 
1103  // ... otherwise take time as seconds and use the specified reference
1104  // system. If no TT, UTC or TAI arrtibutes are specified use the value
1105  // specified by the reference system.
1106  else {
1107  double timeval = extract_timeval(str);
1108  std::string timesys = extract_timesys(str);
1109  if (timesys.empty()) {
1110  timesys = ref.timesys();
1111  }
1112  GTimeReference timeref(ref.mjdref(), ref.timeunit(), timesys, ref.timeref());
1113  set(timeval, timeref);
1114  }
1115 
1116  // Return
1117  return;
1118 }
1119 
1120 
1121 /***********************************************************************//**
1122  * @brief Set time to current time
1123  *
1124  * Sets time to current time.
1125  ***************************************************************************/
1126 void GTime::now(void)
1127 {
1128  // Allocate variables
1129  struct std::tm timeStruct;
1130  std::time_t now;
1131  char buffer[100];
1132 
1133  // Get time
1134  now = std::time(NULL);
1135  #ifdef HAVE_GMTIME_R
1136  std::gmtime_r(&now, &timeStruct);
1137  #else
1138  std::memcpy(&timeStruct, gmtime(&now), sizeof(struct tm));
1139  #endif
1140 
1141  // Write message type, time and task name to buffer
1142  std::sprintf(buffer, "%04d-%02d-%02dT%02d:%02d:%02d",
1143  timeStruct.tm_year + 1900,
1144  timeStruct.tm_mon + 1,
1145  timeStruct.tm_mday,
1146  timeStruct.tm_hour,
1147  timeStruct.tm_min,
1148  timeStruct.tm_sec);
1149 
1150  // Build string from buffer
1151  std::string date = buffer;
1152 
1153  // Set UTC time
1154  utc(date);
1155 
1156  // Return
1157  return;
1158 }
1159 
1160 
1161 /***********************************************************************//**
1162  * @brief Returns native time reference
1163  *
1164  * @return Native time reference.
1165  *
1166  * Returns the native GammaLib time reference. The GammaLib native time
1167  * reference (i.e. time=0) is defined as January 1, 2010, 00:00:00 (TT).
1168  * The time system is Terrestrial Time (TT). Time is stored in seconds.
1169  ***************************************************************************/
1171 {
1172  // Allocate native time reference
1173  GTimeReference reference(mjd_ref, "s", "TT", "LOCAL");
1174 
1175  // Return reference
1176  return reference;
1177 }
1178 
1179 
1180 /***********************************************************************//**
1181  * @brief Print time
1182  *
1183  * @param[in] chatter Chattiness.
1184  * @return String containing time in seconds in native reference.
1185  *
1186  * Prints time in seconds in the native reference.
1187  ***************************************************************************/
1188 std::string GTime::print(const GChatter& chatter) const
1189 {
1190  // Initialise result string
1191  std::string result;
1192 
1193  // Continue only if chatter is not silent
1194  if (chatter != SILENT) {
1195 
1196  // Append time
1197  result.append(gammalib::str(m_time)+" s (TT)");
1198 
1199  } // endif: chatter was not silent
1200 
1201  // Return
1202  return result;
1203 }
1204 
1205 
1206 /*==========================================================================
1207  = =
1208  = Private methods =
1209  = =
1210  ==========================================================================*/
1211 
1212 /***********************************************************************//**
1213  * @brief Initialise class members
1214  ***************************************************************************/
1216 {
1217  // Initialise members
1218  m_time = 0.0;
1219 
1220  // Return
1221  return;
1222 }
1223 
1224 
1225 /***********************************************************************//**
1226  * @brief Copy class members
1227  *
1228  * @param[in] time Time.
1229  ***************************************************************************/
1230 void GTime::copy_members(const GTime& time)
1231 {
1232  // Copy time
1233  m_time = time.m_time;
1234 
1235  // Return
1236  return;
1237 }
1238 
1239 
1240 /***********************************************************************//**
1241  * @brief Delete class members
1242  ***************************************************************************/
1244 {
1245  // Return
1246  return;
1247 }
1248 
1249 
1250 /***********************************************************************//**
1251  * @brief Returns number of leap seconds for a given MJD
1252  *
1253  * @param[in] mjd Modified Julian Day in UTC time system.
1254  * @return Number of lead seconds.
1255  *
1256  * Return the number of leap seconds for a given MJD specified in the UTC
1257  * time system. This method returns valid number of leap seconds for the
1258  * years 1972-2017.
1259  *
1260  * See http://www.nist.gov/pml/div688/grp50/leapsecond.cfm for a table of
1261  * leap seconds.
1262  ***************************************************************************/
1263 double GTime::leap_seconds(const double& mjd) const
1264 {
1265  // Leap second table from 1972 on
1266  // see http://www.nist.gov/pml/div688/grp50/leapsecond.cfm
1267  // The first entry is 1-Jan-1972
1268  const long leapsmjd[] = {41317, // 1972-01-01
1269  41498, // 1972-06-30
1270  41682, // 1972-12-31
1271  42047, // 1973-12-31
1272  42412, // 1974-12-31
1273  42777, // 1975-12-31
1274  43143, // 1976-12-31
1275  43508, // 1977-12-31
1276  43873, // 1978-12-31
1277  44238, // 1979-12-31
1278  44785, // 1981-06-30
1279  45150, // 1982-06-30
1280  45515, // 1983-06-30
1281  46246, // 1985-06-30
1282  47160, // 1987-12-31
1283  47891, // 1989-12-31
1284  48256, // 1990-12-31
1285  48803, // 1992-06-30
1286  49168, // 1993-06-30
1287  49533, // 1994-06-30
1288  50082, // 1995-12-31
1289  50629, // 1997-06-30
1290  51178, // 1998-12-31
1291  53735, // 2005-12-31
1292  54831, // 2008-12-31
1293  56108, // 2012-06-30
1294  57203, // 2015-06-30
1295  57753}; // 2016-12-31
1296  const double leapsecs[] = {10.0, 11.0, 12.0, 13.0, 14.0,
1297  15.0, 16.0, 17.0, 18.0, 19.0,
1298  20.0, 21.0, 22.0, 23.0, 24.0,
1299  25.0, 26.0, 27.0, 28.0, 29.0,
1300  30.0, 31.0, 32.0, 33.0, 34.0,
1301  35.0, 36.0, 37.0};
1302  const int n_leapsecs = sizeof(leapsmjd)/sizeof(long);
1303 
1304  // Extract MJD day and MJD fraction
1305  long day = (long)mjd;
1306 
1307  // Find the leap second MJD that is equal or larger to the specified
1308  // day
1309  int i = n_leapsecs - 1;
1310  while ((day <= leapsmjd[i]) && i > 0) {
1311  i--;
1312  }
1313 
1314  // Return leap seconds
1315  return (leapsecs[i]);
1316 }
1317 
1318 
1319 /***********************************************************************//**
1320  * @brief Extract time value from time string
1321  *
1322  * @param[in] time Time string.
1323  * @return Time value.
1324  *
1325  * Extracts the time value from a time string. The method strips any prefix
1326  * such as "MJD" or "JD" and any suffix starting with a left parentheses "("
1327  * and converts the remainder into a double precision value.
1328  ***************************************************************************/
1329 double GTime::extract_timeval(const std::string& time) const
1330 {
1331  // Find time
1332  size_t length = time.length();
1333  size_t start = time.find_first_of("0123456789+-.");
1334  size_t stop = time.find("(");
1335 
1336  // If there is a stop position then set length to stop
1337  if (stop != std::string::npos) {
1338  length = stop;
1339  }
1340 
1341  // Reduce length by start
1342  if (start != std::string::npos) {
1343  length -= start;
1344  }
1345 
1346  // Get substring containing the time value
1347  std::string value = time.substr(start, length);
1348 
1349  // Convert value into double precision
1350  double result = gammalib::todouble(value);
1351 
1352  // Return result
1353  return result;
1354 }
1355 
1356 
1357 /***********************************************************************//**
1358  * @brief Extract time system from time string
1359  *
1360  * @param[in] time Time string.
1361  * @return Time system.
1362  *
1363  * Extracts the time system from a time string. Valid time systems are:
1364  *
1365  * "(TT)" (TT system)
1366  * "(UTC)" (UTC system)
1367  * "(TAI)" (TAI system)
1368  *
1369  * If no time system is found a blank string is returned.
1370  ***************************************************************************/
1371 std::string GTime::extract_timesys(const std::string& time) const
1372 {
1373  // Initialise time system with blank string
1374  std::string timesys = "";
1375 
1376  // Strip any whitespace from string and convert it to upper case
1377  std::string str = gammalib::toupper(gammalib::strip_whitespace(time));
1378 
1379  // Check for UTC
1380  if (str.find("(UTC)") != std::string::npos) {
1381  timesys = "UTC";
1382  }
1383  else if (str.find("(TAI)") != std::string::npos) {
1384  timesys = "TAI";
1385  }
1386  else if (str.find("(TT)") != std::string::npos) {
1387  timesys = "TT";
1388  }
1389 
1390  // Return time system
1391  return timesys;
1392 }
std::string extract_timesys(const std::string &time) const
Extract time system from time string.
Definition: GTime.cpp:1371
#define G_SECS_GET
Definition: GTime.cpp:46
void init_members(void)
Initialise class members.
Definition: GTime.cpp:1215
void now(void)
Set time to current time.
Definition: GTime.cpp:1126
GTimeReference reference(void) const
Returns native time reference.
Definition: GTime.cpp:1170
const std::string & timeunit(void) const
Return time unit.
const double mjd_ref
MJD of time=0.
Definition: GTime.cpp:41
double lmst(const double &geolon) const
Return local mean sidereal time in hours in a day.
Definition: GTime.cpp:656
void copy_members(const GTime &time)
Copy class members.
Definition: GTime.cpp:1230
GVector cos(const GVector &vector)
Computes cosine of vector elements.
Definition: GVector.cpp:1190
double unitseconds(void) const
Return the time unit in seconds.
void clear(void)
Clear time.
Definition: GTime.cpp:252
const double sec2day
Definition: GTools.hpp:50
Time class.
Definition: GTime.hpp:55
Gammalib tools definition.
std::string strip_whitespace(const std::string &arg)
Strip leading and trailing whitespace from string.
Definition: GTools.cpp:80
#define G_SECS_SET
Definition: GTime.cpp:47
double extract_timeval(const std::string &time) const
Extract time value from time string.
Definition: GTime.cpp:1329
double jd(void) const
Return time in Julian Days (TT)
Definition: GTime.cpp:284
bool is_leap_year(const int &year) const
Signals if year is a leap year.
Definition: GTime.hpp:210
#define G_UTC_GET
Definition: GTime.cpp:49
const double sec_in_day
Definition: GTools.hpp:49
virtual ~GTime(void)
Destructor.
Definition: GTime.cpp:200
const double deg2rad
Definition: GMath.hpp:43
const double & mjdref(void) const
Return MJD reference (units: days)
const double jd_ref
JD of time=0.
Definition: GTime.cpp:42
void free_members(void)
Delete class members.
Definition: GTime.cpp:1243
GTime(void)
Void constructor.
Definition: GTime.cpp:67
const double tai2tt
Definition: GTools.hpp:51
Time reference class interface definition.
double leap_seconds(void) const
Return number of leap seconds for current time.
Definition: GTime.hpp:181
const std::string & timesys(void) const
Return time system.
GChatter
Definition: GTypemaps.hpp:33
std::string print(const GChatter &chatter=NORMAL) const
Print time.
Definition: GTime.cpp:1188
const std::string & timeref(void) const
Return time reference.
GTime * clone(void) const
Clone time.
Definition: GTime.cpp:270
double gmst(void) const
Return Greenwich mean sidereal time in hours in a day.
Definition: GTime.cpp:593
const double & secs(void) const
Return time in seconds in native reference (TT)
Definition: GTime.hpp:156
double last(const double &geolon) const
Return local apparent sidereal time in hours in a day.
Definition: GTime.cpp:677
GTime & operator=(const GTime &time)
Assignment operator.
Definition: GTime.cpp:222
double gast(void) const
Return Greenwich apparent sidereal time in hours in a day.
Definition: GTime.cpp:616
GVector sin(const GVector &vector)
Computes sine of vector elements.
Definition: GVector.cpp:1316
Exception handler interface definition.
std::string toupper(const std::string &s)
Convert string to upper case.
Definition: GTools.cpp:941
Implements a time reference.
std::string tolower(const std::string &s)
Convert string to lower case.
Definition: GTools.cpp:955
double mjd(void) const
Return time in Modified Julian Days (TT)
Definition: GTime.cpp:320
#define G_CONSTRUCT
Definition: GTime.cpp:45
int days_in_year(const int &year) const
Returns number of days in year.
Definition: GTime.hpp:225
#define G_UTC
Definition: GTime.cpp:48
double julian_epoch(void) const
Return Julian epoch in native reference (TT)
Definition: GTime.cpp:425
std::string utc(const int &precision=0) const
Return time as string in UTC time system.
Definition: GTime.cpp:465
Time class interface definition.
void set(const double &time, const GTimeReference &ref)
Set time given in specified reference.
Definition: GTime.cpp:1005
double convert(const GTimeReference &ref) const
Return time in specified reference.
Definition: GTime.cpp:698
double days(void) const
Return time in days in native reference (TT)
Definition: GTime.cpp:400
double m_time
Time in seconds in native reference (TT)
Definition: GTime.hpp:134
Mathematical function definitions.
double todouble(const std::string &arg)
Convert string into double precision value.
Definition: GTools.cpp:926
std::string str(const unsigned short int &value)
Convert unsigned short integer value into string.
Definition: GTools.cpp:489