julian-date.cc
Go to the documentation of this file.
1 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2 /*
3  * Copyright (c) 2016 INESC TEC
4  * Copyright (c) 2021 CNES
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 as
8  * published by the Free Software Foundation;
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18  *
19  * Code from https://gitlab.inesctec.pt/pmms/ns3-satellite
20  *
21  * Author: Pedro Silva <pmms@inesctec.pt>
22  * Author: Bastien Tauran <bastien.tauran@viveris.fr>
23  */
24 
25 #include "julian-date.h"
26 
27 #include "iers-data.h"
28 #include "satellite-sgp4ext.h"
29 #include "satellite-sgp4unit.h"
30 
31 #include <ns3/assert.h>
32 #include <ns3/nstime.h>
33 
34 #include <cmath>
35 #include <cstdio>
36 #include <iomanip>
37 #include <sstream>
38 #include <stdint.h>
39 #include <string>
40 
41 namespace ns3
42 {
43 
44 const uint32_t JulianDate::PosixYear = 1970;
45 const uint32_t JulianDate::MinYear = 1992;
46 const uint32_t JulianDate::MaxYear = 2099;
47 const double JulianDate::PosixEpoch = 2440587.5;
48 const uint32_t JulianDate::J2000Epoch = 2451545;
49 const uint32_t JulianDate::Posix1992 = 8035;
50 const uint32_t JulianDate::HourToMs = 3600000;
51 const uint32_t JulianDate::DayToMs = HourToMs * 24;
52 const Time JulianDate::TtToTai = MilliSeconds(32184);
53 const Time JulianDate::TaiToGps = MilliSeconds(19000);
54 
55 std::ostream&
56 operator<<(std::ostream& os, DateTime::TimeSystem ts)
57 {
58  switch (ts)
59  {
60  case DateTime::UTC:
61  os << "UTC";
62  break;
63  case DateTime::TAI:
64  os << "TAI";
65  break;
66  case DateTime::TT:
67  os << "TT";
68  break;
69  case DateTime::UT1:
70  os << "UT1";
71  break;
72  case DateTime::GPST:
73  os << "GPS";
74  break;
75  case DateTime::POSIX:
76  os << "UTC";
77  break; // POSIX is printed as UTC
78  default:
79  break;
80  }
81 
82  return os;
83 }
84 
86 {
87  // the date since complete IERS data is available
88  m_days = static_cast<uint32_t>((MinYear - PosixYear) * 365.25);
89  m_ms_day = 0;
91 }
92 
94 {
95  SetDate(jd);
96 }
97 
98 JulianDate::JulianDate(uint32_t days, uint32_t ms_day)
99 {
100  SetDate(days, ms_day);
101 }
102 
103 JulianDate::JulianDate(const std::string& date, TimeSystem ts)
104 {
105  SetDate(date, ts);
106 }
107 
108 double
110 {
111  double base = m_days + m_ms_day / static_cast<double>(DayToMs);
112 
113  if (ts != DateTime::POSIX)
114  return base += PosixEpoch;
115 
116  // transform base into Julian date, and add offset (if any)
117  return base + OffsetFromUtc(m_days, ts).GetDays();
118 }
119 
120 DateTime
122 {
123  return GregorianDate();
124 }
125 
126 DateTime
128 {
129  return (*this + OffsetFromUtc(m_days, ts)).GregorianDate();
130 }
131 
132 std::string
134 {
135  std::ostringstream oss;
136  DateTime dt = (*this + OffsetFromUtc()).GregorianDate();
137 
138  oss << std::setfill('0');
139  oss << std::setw(4) << dt.year << "-" << std::setw(2) << dt.month << "-" << std::setw(2)
140  << dt.day << " " << std::setw(2) << dt.hours << ":" << std::setw(2) << dt.minutes << ":"
141  << std::setw(2) << dt.seconds << "." << std::setw(3) << dt.millisecs << " " << m_time_scale;
142 
143  return oss.str();
144 }
145 
146 std::string
148 {
149  JulianDate jd = *this;
150 
151  jd.m_time_scale = ts;
152 
153  return jd.ToString();
154 }
155 
156 std::pair<double, double>
158 {
159  const std::vector<IersData::EopParameters>& v = IersData::EopValues;
160  uint32_t pos = m_days - Posix1992; // full days since 01 Jan 1992
161 
162  // if there is data available
163  if (pos < v.size())
164  return std::make_pair(v[pos].xp, v[pos].yp);
165 
166  return std::make_pair(0, 0);
167 }
168 
169 double
171 {
172  const std::vector<IersData::EopParameters>& v = IersData::EopValues;
173  uint32_t pos = m_days - Posix1992; // full days since 1 Jan 1992
174  double lod = (pos < v.size() ? v[pos].lod : 0); // LOD in milliseconds
175 
176  // use IERS angular velocity formula with extra precision if LOD is available
177  // LOD is in milliseconds, and the result is in radians/s
178  return 7.2921151467064e-5 * (1.0 - lod / DayToMs);
179 }
180 
181 // from http://aa.usno.navy.mil/faq/docs/GAST.php
182 double
184 {
186  return gstime((*this + Dut1(m_days)).GetDouble());
187 
189 
190  // JulianDate jdut1 = *this + Dut1 (m_days);
191 
192  // from http://aa.usno.navy.mil/faq/docs/GAST.php
193  //
194  // 6.697374558 + 0.06570982441908*D0 + 1.00273790935*H + 0.000026*T²
195  //
196  // JD0 = jdut1.m_days + PosixEpoch
197  // D0 = jdut1.m_days + PosixEpoch - J2000Epoch
198  // H = jdut1.m_ms_day/HourToMs
199  // D = D0 + H/24
200  // T = D/36525.0 = (D0 + jdut1.m_ms_day/DayToMs)/36525.0
201 
202  // double d0 = jdut1.m_days + PosixEpoch - J2000Epoch;
203  // double h = jdut1.m_ms_day/static_cast<double> (HourToMs);
204  // double t = (d0 + jdut1.m_ms_day/static_cast<double> (DayToMs))/36525.0;
205  // double gmst = 6.697374558 + 0.06570982441908*d0 + 1.00273790935*h +
206  // 0.000026*t*t;
207 
208  // gmst = fmod(gmst * M_PI/12, 2*M_PI);
209 
210  // return (gmst < 0 ? gmst+2*M_PI : gmst);
211 }
212 
213 void
215 {
216  // POSIX/Unix epoch is used internally
217  jd -= PosixEpoch;
218 
219  m_days = static_cast<uint32_t>(jd);
220  m_ms_day = static_cast<uint32_t>((jd - m_days) * DayToMs);
222 }
223 
224 void
225 JulianDate::SetDate(uint32_t days, uint32_t ms_day)
226 {
227  // POSIX/Unix epoch is used internally
228  m_days = days;
229  m_ms_day = ms_day;
231 }
232 
233 void
234 JulianDate::SetDate(const std::string& date, TimeSystem ts)
235 {
236  double seconds;
237  DateTime dt;
238 
239  NS_ASSERT_MSG(ts != DateTime::POSIX, "POSIX/Unix time scale is not valid for dates.");
240 
241  // YYYY-MM-DD HH:MM:SS(.MMM)
242  std::sscanf(date.c_str(),
243  "%04d%*c%02d%*c%02d %02d%*c%02d%*c%lf",
244  &dt.year,
245  &dt.month,
246  &dt.day,
247  &dt.hours,
248  &dt.minutes,
249  &seconds);
250 
251  NS_ASSERT_MSG(dt.year >= MinYear, "Complete EOP data is not available before that date!");
252 
253  NS_ASSERT_MSG(dt.year <= MaxYear, "Dates beyond 2099 are not supported!");
254 
255  dt.seconds = static_cast<uint32_t>(seconds);
256  dt.millisecs = static_cast<uint32_t>((seconds - dt.seconds) * 1000 + 0.5);
257 
258  // based on formula from http://aa.usno.navy.mil/faq/docs/JD_Formula.php
259  m_days = 367 * dt.year - 7 * (dt.year + (dt.month + 9) / 12) / 4 + 275 * dt.month / 9 + dt.day -
260  static_cast<uint32_t>(PosixEpoch - 1721013.5);
261  m_ms_day = (dt.hours * 3600 + dt.minutes * 60 + dt.seconds) * 1000 + dt.millisecs;
262  m_time_scale = ts;
263 
264  *this += OffsetToUtc();
265 }
266 
268 JulianDate::operator+(const Time& t) const
269 {
270  JulianDate jd;
271 
272  // if time is negative, call operator- with a positive time
273  if (t.IsStrictlyNegative())
274  return *this - MilliSeconds(-t.GetMilliSeconds());
275 
276  jd.m_days = static_cast<uint32_t>(t.GetDays());
277  jd.m_ms_day = static_cast<uint32_t>(t.GetMilliSeconds() % DayToMs);
278 
279  jd.m_ms_day += m_ms_day;
280  jd.m_days += m_days + jd.m_ms_day / DayToMs;
281  jd.m_ms_day %= DayToMs;
283 
284  return jd;
285 }
286 
287 void
289 {
290  *this = *this + t;
291 }
292 
294 JulianDate::operator-(const Time& t) const
295 {
296  JulianDate jd;
297 
298  // if time is negative, call operator+ with a positive time
299  if (t.IsStrictlyNegative())
300  return *this + MilliSeconds(-t.GetMilliSeconds());
301 
302  jd.m_days = static_cast<uint32_t>(t.GetDays());
303  jd.m_ms_day = static_cast<uint32_t>(t.GetMilliSeconds() % DayToMs);
304 
305  if (jd.m_ms_day > m_ms_day)
306  {
307  jd.m_ms_day = DayToMs - (jd.m_ms_day - m_ms_day);
308  jd.m_days += 1;
309  }
310  else
311  jd.m_ms_day = m_ms_day - jd.m_ms_day;
312 
313  jd.m_days = m_days - jd.m_days;
315 
316  return jd;
317 }
318 
319 void
321 {
322  *this = *this - t;
323 }
324 
325 Time
327 {
328  Time t1 = ns3::Days(m_days) + ns3::MilliSeconds(m_ms_day);
329  Time t2 = ns3::Days(jd.m_days) + ns3::MilliSeconds(jd.m_ms_day);
330 
331  return t1 - t2;
332 }
333 
334 bool
335 JulianDate::operator<(const JulianDate& jd) const
336 {
337  if (m_days != jd.m_days)
338  return m_days < jd.m_days;
339 
340  return m_ms_day < jd.m_ms_day;
341 }
342 
343 bool
344 JulianDate::operator<=(const JulianDate& jd) const
345 {
346  if (m_days != jd.m_days)
347  return m_days < jd.m_days;
348 
349  return m_ms_day <= jd.m_ms_day;
350 }
351 
352 bool
354 {
355  if (m_days != jd.m_days)
356  return m_days > jd.m_days;
357 
358  return m_ms_day > jd.m_ms_day;
359 }
360 
361 bool
363 {
364  if (m_days != jd.m_days)
365  return m_days > jd.m_days;
366 
367  return m_ms_day >= jd.m_ms_day;
368 }
369 
370 bool
372 {
373  if (m_days != jd.m_days)
374  return false;
375 
376  return m_ms_day == jd.m_ms_day;
377 }
378 
379 bool
381 {
382  if (m_days != jd.m_days)
383  return true;
384 
385  return m_ms_day != jd.m_ms_day;
386 }
387 
388 std::ostream&
389 operator<<(std::ostream& os, const JulianDate& jd)
390 {
391  os << jd.ToString();
392 
393  return os;
394 }
395 
396 //
397 //
398 //
399 
400 bool
402 {
403  // if the year is divisible by 4, it is a leap year
404  // [this works because we only consider dates between 1992 and 2099]
405  return ((year & 0x03) == 0);
406 }
407 
408 Time
409 JulianDate::TaiMinusUtc(uint32_t daysInPosix)
410 {
411  const std::vector<uint32_t>& v = IersData::LeapSeconds;
412  std::vector<uint32_t>::const_iterator it;
413 
414  it = std::lower_bound(v.begin(), v.end(), daysInPosix);
415 
416  return MilliSeconds(((it - v.begin()) + IersData::BaseLeapSeconds) * 1000);
417 }
418 
419 Time
420 JulianDate::Dut1(uint32_t daysInPosix)
421 {
422  const std::vector<IersData::EopParameters>& v = IersData::EopValues;
423  uint32_t pos = daysInPosix - Posix1992; // full days since 01 Jan 1992
424 
425  return MilliSeconds(pos < v.size() ? v[pos].dut1 * 1000 : 0);
426 }
427 
428 DateTime
430 {
431  return GregorianDate(m_days, m_ms_day);
432 }
433 
434 DateTime
435 JulianDate::GregorianDate(uint32_t days, uint32_t ms_day)
436 {
437  uint32_t month_days[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
438  uint32_t d = days - Posix1992, days_of_year, leap_years;
439  Time t = MilliSeconds(ms_day);
440  DateTime dt;
441 
442  // this formula only works if we consider an year that is multiple of 4
443  dt.year = static_cast<uint32_t>(d / 365.25) + MinYear;
444 
445  leap_years = (dt.year - (MinYear + 1)) / 4;
446  days_of_year = d - ((dt.year - MinYear) * 365 + leap_years);
447 
448  // if it is a leap year, February has 29 days
449  if (IsLeapYear(dt.year))
450  month_days[1] += 1;
451 
452  for (uint32_t i = 0; i < 12; ++i)
453  {
454  if (days_of_year <= month_days[i])
455  {
456  dt.month = i + 1;
457 
458  break;
459  }
460 
461  days_of_year -= month_days[i];
462  }
463 
464  dt.day = days_of_year;
465 
466  dt.hours = static_cast<uint32_t>(t.GetHours());
467  t -= Hours(dt.hours);
468 
469  dt.minutes = static_cast<uint32_t>(t.GetMinutes());
470  t -= Minutes(dt.minutes);
471 
472  dt.seconds = static_cast<uint32_t>(t.GetSeconds());
473  t -= Seconds(dt.seconds);
474 
475  dt.millisecs = static_cast<uint32_t>(t.GetMilliSeconds());
476 
477  return dt;
478 }
479 
480 Time
482 {
484 }
485 
486 Time
487 JulianDate::OffsetFromUtc(uint32_t daysInPosix, TimeSystem ts)
488 {
489  // already in sync for UTC and POSIX
490  Time offset = MilliSeconds(0);
491 
492  switch (ts)
493  {
494  case DateTime::UT1:
495  offset = Dut1(daysInPosix);
496  break;
497  case DateTime::TAI:
498  offset = TaiMinusUtc(daysInPosix);
499  break;
500  case DateTime::TT:
501  offset = TtToTai + TaiMinusUtc(daysInPosix);
502  break;
503  case DateTime::GPST:
504  offset = TaiMinusUtc(daysInPosix) - TaiToGps;
505  break;
506  default:
507  break;
508  }
509 
510  return offset;
511 }
512 
513 Time
515 {
517 }
518 
519 Time
520 JulianDate::OffsetToUtc(uint32_t daysInPosix, uint32_t ms_day, TimeSystem ts)
521 {
522  // already in sync
523  if (ts == DateTime::UTC || ts == DateTime::POSIX)
524  return MilliSeconds(0);
525 
526  Time tai_utc = TaiMinusUtc(daysInPosix);
527  Time offset = (ts == DateTime::TT ? TtToTai : MilliSeconds(0));
528 
529  // if it is not the same day in UTC, check the leap secs for the previous day
530  if (ms_day < (offset + tai_utc).GetMilliSeconds())
531  tai_utc = TaiMinusUtc(daysInPosix - 1);
532 
533  switch (ts)
534  {
535  case DateTime::UT1:
536  offset = Dut1(daysInPosix);
537  break;
538  case DateTime::TAI:
539  offset = tai_utc;
540  break;
541  case DateTime::TT:
542  offset = TtToTai + tai_utc;
543  break;
544  case DateTime::GPST:
545  offset = tai_utc - TaiToGps;
546  break;
547  default:
548  break;
549  }
550 
551  // return the offset as negative
552  return MilliSeconds(-offset.GetMilliSeconds());
553 }
554 
555 } // namespace ns3
static const std::vector< uint32_t > LeapSeconds
Definition: iers-data.h:53
static const std::vector< EopParameters > EopValues
Definition: iers-data.h:50
static const uint32_t BaseLeapSeconds
Definition: iers-data.h:51
Class to handle Julian days and provide respective Earth Orientation Parameters (EOP).
Definition: julian-date.h:78
static bool IsLeapYear(uint32_t year)
Check if it is a leap year.
Definition: julian-date.cc:401
JulianDate(void)
Default constructor.
Definition: julian-date.cc:85
double GetGmst(void) const
Retrieve the Greenwich Mean Sidereal Time.
Definition: julian-date.cc:183
static const Time TaiToGps
offset from TAI to GPST
Definition: julian-date.h:92
void SetDate(double jd)
Set the Julian days.
Definition: julian-date.cc:214
uint32_t m_days
full days since epoch.
Definition: julian-date.h:370
double GetDouble(TimeSystem ts=DateTime::UTC) const
GetDouble Get the Julian days.
Definition: julian-date.cc:109
bool operator>(const JulianDate &jd) const
operator > (compare Julian dates).
Definition: julian-date.cc:353
bool operator==(const JulianDate &jd) const
operator == (compare Julian dates).
Definition: julian-date.cc:371
JulianDate operator-(const Time &t) const
operator - (negative time adjustment).
Definition: julian-date.cc:294
JulianDate operator+(const Time &t) const
operator + (positive time adjustment).
Definition: julian-date.cc:268
Time OffsetFromUtc(void) const
Returns the offset from UTC.
Definition: julian-date.cc:481
TimeSystem m_time_scale
external time system.
Definition: julian-date.h:372
static const uint32_t PosixYear
POSIX/Unix epoch year.
Definition: julian-date.h:83
static const uint32_t DayToMs
milliseconds in a day
Definition: julian-date.h:90
static const uint32_t MinYear
Minimum year supported.
Definition: julian-date.h:84
Time OffsetToUtc(void) const
Returns the offset to UTC.
Definition: julian-date.cc:514
static Time TaiMinusUtc(uint32_t daysInPosix)
The difference between TAI and UTC time systems.
Definition: julian-date.cc:409
static const Time TtToTai
offset from TT to TAI
Definition: julian-date.h:91
static const double PosixEpoch
1 Jan 1970, 0h
Definition: julian-date.h:86
uint32_t m_ms_day
milliseconds of the day.
Definition: julian-date.h:371
static const uint32_t HourToMs
milliseconds in an hour
Definition: julian-date.h:89
std::string ToString(void) const
Get the string representation of the Gregorian calendar date.
Definition: julian-date.cc:133
DateTime GetDateTime(void) const
Get the Gregorian calendar date in current time system.
Definition: julian-date.cc:121
bool operator<(const JulianDate &jd) const
operator < (compare Julian dates).
Definition: julian-date.cc:335
double GetOmegaEarth(void) const
Retrieve Earth's angular velocity.
Definition: julian-date.cc:170
bool operator!=(const JulianDate &jd) const
operator != (compare Julian dates).
Definition: julian-date.cc:380
static Time Dut1(uint32_t daysInPosix)
Retrieve the DUT1 (UT1 - UTC) value for a given day.
Definition: julian-date.cc:420
std::pair< double, double > GetPolarMotion(void) const
Retrieve the polar motion cofficients measured/predicted.
Definition: julian-date.cc:157
DateTime GregorianDate(void) const
Convert into Gregorian calendar.
Definition: julian-date.cc:429
bool operator<=(const JulianDate &jd) const
operator <= (compare Julian dates).
Definition: julian-date.cc:344
bool operator>=(const JulianDate &jd) const
operator >= (compare Julian dates).
Definition: julian-date.cc:362
void operator+=(const Time &t)
operator += (positive time adjustment).
Definition: julian-date.cc:288
static const uint32_t J2000Epoch
1 Jan 2000, 12h
Definition: julian-date.h:87
void operator-=(const Time &t)
operator -= (negative time adjustment).
Definition: julian-date.cc:320
static const uint32_t Posix1992
1 Jan 1992 (POSIX days)
Definition: julian-date.h:88
static const uint32_t MaxYear
Maximum year supported.
Definition: julian-date.h:85
SatArqSequenceNumber is handling the sequence numbers for the ARQ process.
std::ostream & operator<<(std::ostream &os, const GeoCoordinate &coordinate)
double gstime(double jdut1)
The DateTime struct.
Definition: julian-date.h:45
uint32_t year
Definition: julian-date.h:56
uint32_t day
Definition: julian-date.h:56
uint32_t minutes
Definition: julian-date.h:57
@ POSIX
Unix/POSIX Time.
Definition: julian-date.h:53
@ UTC
Coordinated Universal Time [French: Temps Universel Coordonné].
Definition: julian-date.h:48
@ TAI
International Atomic Time [French: Temps Atomique International].
Definition: julian-date.h:50
@ TT
Terrestrial Time.
Definition: julian-date.h:51
@ UT1
Universal Time.
Definition: julian-date.h:49
@ GPST
Global Positioning System (GPS) Time.
Definition: julian-date.h:52
uint32_t seconds
Definition: julian-date.h:57
uint32_t month
Definition: julian-date.h:56
uint32_t hours
Definition: julian-date.h:57
uint32_t millisecs
Definition: julian-date.h:57