/ 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