/ decoder.cpp
decoder.cpp
  1  // SPDX-FileCopyrightText: 2021 Jeff Epler
  2  //
  3  // SPDX-License-Identifier: GPL-3.0-only
  4  
  5  #ifndef MAIN
  6  #define NDEBUG
  7  #endif
  8  
  9  #include "decoder.h"
 10  
 11  time_t wwvb_time::to_utc() const {
 12      struct tm tm = {};
 13      tm.tm_year = 100 + year;
 14      tm.tm_mday = 1;
 15      time_t t = mktime(&tm);
 16      t += (yday - 1) * 86400 + hour * 3600 + minute * 60;
 17      t += (second == 60) ? 59 : second;
 18      return t;
 19  }
 20  
 21  struct tm wwvb_time::apply_zone_and_dst(int zone_offset,
 22                                          bool observe_dst) const {
 23      auto t = to_utc();
 24      t -= zone_offset * 3600;
 25  
 26      struct tm tm;
 27      gmtime_r(&t, &tm);
 28  
 29      switch (dst) {
 30      case 0: // Standard time in effect
 31          observe_dst = false;
 32  
 33          break;
 34      case 1: // DST ending at
 35      {
 36          struct tm tt = tm;
 37          tt.tm_hour = 1; // DST ends at 2AM *DST* which is 1AM *standard*
 38          auto u = mktime(&tt);
 39          if (t >= u)
 40              observe_dst = false;
 41      }
 42  
 43      break;
 44      case 2: // DST starting at 2AM local time this day
 45      {
 46          struct tm tt = tm;
 47          tt.tm_hour = 2;
 48          auto u = mktime(&tt);
 49          if (t < u)
 50              observe_dst = false;
 51      }
 52      }
 53  
 54      if (observe_dst)
 55          t += 3600;
 56  
 57      gmtime_r(&t, &tm);
 58      tm.tm_isdst = observe_dst;
 59      if (second == 60) {
 60          tm.tm_sec++;
 61      }
 62      return tm;
 63  }
 64  
 65  int wwvb_time::seconds_in_minute() const {
 66      // todo: support for negative leap seconds
 67      if (ls && hour == 23 && minute == 59) {
 68          int y = yday - ly;
 69          if (y == 181 || y == 365)
 70              return dut1 < 0 ? 61 : 59;
 71      }
 72      return 60;
 73  }
 74  
 75  void wwvb_time::advance_seconds(int n) {
 76      int second = this->second + n;
 77      while (second >= seconds_in_minute()) {
 78          // If there's the possibility of a leap seconds, must do advance a
 79          // single minute at a time
 80          if (ls) {
 81              second -= seconds_in_minute();
 82              advance_minutes();
 83          } else {
 84              // Otherwise we can advance knowing all minutes have 60 seconds
 85              advance_minutes(second / 60);
 86              second %= 60;
 87          }
 88      }
 89      this->second = second;
 90  }
 91  
 92  // this is a 2000-based year!
 93  static bool isly(int year) {
 94      if (year % 400)
 95          return true;
 96      if (year % 100)
 97          return false;
 98      if (year % 4)
 99          return false;
100      return true;
101  }
102  
103  static int last_yday(int year) { return 365 + isly(year); }
104  
105  // advance to exactly the top of the n'th minute from now
106  void wwvb_time::advance_minutes(int n) {
107      if (seconds_in_minute() != 60) {
108          ls = 0;
109          if (dut1 < 0)
110              dut1 += 10;
111          else if (dut1 > 0)
112              dut1 -= 10;
113      }
114  
115      second = 0;
116      minute++;
117      if (minute < 60)
118          return;
119  
120      minute = 0;
121      hour++;
122      if (hour < 24)
123          return;
124  
125      hour = 0;
126      yday++;
127      if (dst == 1)
128          dst = 0;
129      else if (dst == 2)
130          dst = 3;
131      if (yday < last_yday(year))
132          return;
133  
134      yday = 1;
135      year += 1;
136      ly = isly(year);
137  }
138  
139  #if MAIN
140  #include <iostream>
141  using namespace std;
142  
143  int main() {
144      WWVBDecoder<> dec;
145  
146      static char zone[] = "TZ=UTC";
147      putenv(zone);
148      tzset();
149  
150      int i = 0, si = 0, d = 0;
151      for (int c; (c = cin.get()) != EOF;) {
152          if (c != '_' && c != '#') {
153              continue;
154          }
155          if (dec.update(c == '_')) {
156              si++;
157              auto sym = dec.symbols.at(dec.SYMBOLS - 1);
158              // std::cout << sym;
159              // if(si % 60 == 0) std::cout << "\n";
160              if (sym == 2) {
161                  // This mark could be the minute-ending mark, so try to
162                  // decode a minute
163                  wwvb_time m;
164                  if (dec.decode_minute(m)) {
165                      d++;
166                      time_t t = m.to_utc();
167                      struct tm tt;
168                      gmtime_r(&t, &tt);
169                      printf("[%7.2f] %4d-%02d-%02d %2d:%02d %d %d\n", i / 50.,
170                             1900 + tt.tm_year, tt.tm_mon + 1, tt.tm_mday,
171                             tt.tm_hour, tt.tm_min, m.ly, m.dst);
172                      tt = m.apply_zone_and_dst(6, true);
173                      printf("          %4d-%02d-%02d %2d:%02d\n",
174                             1900 + tt.tm_year, tt.tm_mon + 1, tt.tm_mday,
175                             tt.tm_hour, tt.tm_min);
176                      printf("          %4d-%03d   %2d:%02d\n", m.year + 2000,
177                             m.yday, m.hour, m.minute);
178                      printf("Health %4d / %d (%5.2f%%)\n", dec.health,
179                             (int)dec.MAX_HEALTH,
180                             dec.health * 100. / dec.MAX_HEALTH);
181                  }
182              }
183          }
184          i++;
185      }
186      printf(
187          "Samples: %8d Symbols: %7d Minutes: %6d Health: %4d / %d (%5.2f%%)\n",
188          i, si, d, dec.health, (int)dec.MAX_HEALTH,
189          dec.health * 100. / dec.MAX_HEALTH);
190  }
191  #endif