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