wwvb 4.0.0a0__py3-none-any.whl → 4.1.0a0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- leapseconddata/__init__.py +342 -0
- leapseconddata/__main__.py +169 -0
- {wwvb → leapseconddata}/__version__.py +2 -2
- wwvb-4.1.0a0.dist-info/METADATA +60 -0
- wwvb-4.1.0a0.dist-info/RECORD +9 -0
- wwvb-4.1.0a0.dist-info/entry_points.txt +2 -0
- wwvb-4.1.0a0.dist-info/top_level.txt +1 -0
- uwwvb.py +0 -193
- wwvb/__init__.py +0 -935
- wwvb/decode.py +0 -90
- wwvb/dut1table.py +0 -32
- wwvb/gen.py +0 -128
- wwvb/iersdata.py +0 -28
- wwvb/iersdata_dist.py +0 -38
- wwvb/testcli.py +0 -291
- wwvb/testdaylight.py +0 -60
- wwvb/testls.py +0 -63
- wwvb/testpm.py +0 -33
- wwvb/testuwwvb.py +0 -221
- wwvb/testwwvb.py +0 -403
- wwvb/tz.py +0 -13
- wwvb/updateiers.py +0 -199
- wwvb/wwvbtk.py +0 -144
- wwvb-4.0.0a0.dist-info/METADATA +0 -199
- wwvb-4.0.0a0.dist-info/RECORD +0 -23
- wwvb-4.0.0a0.dist-info/entry_points.txt +0 -8
- wwvb-4.0.0a0.dist-info/top_level.txt +0 -2
- {wwvb → leapseconddata}/py.typed +0 -0
- {wwvb-4.0.0a0.dist-info → wwvb-4.1.0a0.dist-info}/WHEEL +0 -0
wwvb/testls.py
DELETED
@@ -1,63 +0,0 @@
|
|
1
|
-
#!/usr/bin/python3
|
2
|
-
"""Leap seconds tests"""
|
3
|
-
|
4
|
-
# SPDX-FileCopyrightText: 2021 Jeff Epler
|
5
|
-
#
|
6
|
-
# SPDX-License-Identifier: GPL-3.0-only
|
7
|
-
|
8
|
-
import datetime
|
9
|
-
import unittest
|
10
|
-
|
11
|
-
import leapseconddata
|
12
|
-
|
13
|
-
import wwvb
|
14
|
-
|
15
|
-
from . import iersdata
|
16
|
-
|
17
|
-
ONE_DAY = datetime.timedelta(days=1)
|
18
|
-
|
19
|
-
|
20
|
-
def next_month(d: datetime.date) -> datetime.date:
|
21
|
-
"""Return the start of the next month after the day 'd'"""
|
22
|
-
d = d.replace(day=28)
|
23
|
-
while True:
|
24
|
-
d0 = d
|
25
|
-
d = d + ONE_DAY
|
26
|
-
if d.month != d0.month:
|
27
|
-
return d
|
28
|
-
|
29
|
-
|
30
|
-
class TestLeapSecond(unittest.TestCase):
|
31
|
-
"""Leap second tests"""
|
32
|
-
|
33
|
-
maxDiff = 9999
|
34
|
-
|
35
|
-
def test_leap(self) -> None:
|
36
|
-
"""Tests that the expected leap seconds all occur."""
|
37
|
-
ls = leapseconddata.LeapSecondData.from_standard_source()
|
38
|
-
assert ls.valid_until is not None
|
39
|
-
|
40
|
-
d = iersdata.start
|
41
|
-
e = min(iersdata.end, ls.valid_until)
|
42
|
-
bench = [ts.start for ts in ls.leap_seconds[1:]]
|
43
|
-
bench = [ts for ts in bench if d <= ts < e]
|
44
|
-
leap = []
|
45
|
-
while d < e:
|
46
|
-
nm = next_month(d)
|
47
|
-
eom = nm - ONE_DAY
|
48
|
-
month_ends_dut1 = wwvb.get_dut1(eom)
|
49
|
-
month_starts_dut1 = wwvb.get_dut1(nm)
|
50
|
-
our_is_ls = month_ends_dut1 * month_starts_dut1 < 0
|
51
|
-
if wwvb.isls(eom):
|
52
|
-
assert our_is_ls
|
53
|
-
self.assertLess(month_ends_dut1, 0)
|
54
|
-
self.assertGreater(month_starts_dut1, 0)
|
55
|
-
leap.append(nm)
|
56
|
-
else:
|
57
|
-
assert not our_is_ls
|
58
|
-
d = datetime.datetime.combine(nm, datetime.time()).replace(tzinfo=datetime.timezone.utc)
|
59
|
-
self.assertEqual(leap, bench)
|
60
|
-
|
61
|
-
|
62
|
-
if __name__ == "__main__": # pragma: no cover
|
63
|
-
unittest.main()
|
wwvb/testpm.py
DELETED
@@ -1,33 +0,0 @@
|
|
1
|
-
#!/usr/bin/python3
|
2
|
-
"""Test Phase Modulation Signal"""
|
3
|
-
|
4
|
-
# SPDX-FileCopyrightText: 2021 Jeff Epler
|
5
|
-
#
|
6
|
-
# SPDX-License-Identifier: GPL-3.0-only
|
7
|
-
|
8
|
-
import unittest
|
9
|
-
|
10
|
-
import wwvb
|
11
|
-
|
12
|
-
|
13
|
-
class TestPhaseModulation(unittest.TestCase):
|
14
|
-
"""Test Phase Modulation Signal"""
|
15
|
-
|
16
|
-
def test_pm(self) -> None:
|
17
|
-
"""Compare the generated signal from a reference minute in NIST docs"""
|
18
|
-
ref_am = "201100000200010011120001010002011000101201000000120010010112"
|
19
|
-
|
20
|
-
ref_pm = "001110110100010010000011001000011000110100110100010110110110"
|
21
|
-
|
22
|
-
ref_minute = wwvb.WWVBMinuteIERS(2012, 186, 17, 30, dst=3)
|
23
|
-
ref_time = ref_minute.as_timecode()
|
24
|
-
|
25
|
-
test_am = ref_time.to_am_string(["0", "1", "2"])
|
26
|
-
test_pm = ref_time.to_pm_string(["0", "1"])
|
27
|
-
|
28
|
-
self.assertEqual(ref_am, test_am)
|
29
|
-
self.assertEqual(ref_pm, test_pm)
|
30
|
-
|
31
|
-
|
32
|
-
if __name__ == "__main__": # pragma: no cover
|
33
|
-
unittest.main()
|
wwvb/testuwwvb.py
DELETED
@@ -1,221 +0,0 @@
|
|
1
|
-
#!/usr/bin/python3
|
2
|
-
"""Test of uwwvb.py"""
|
3
|
-
# SPDX-FileCopyrightText: 2021 Jeff Epler
|
4
|
-
#
|
5
|
-
# SPDX-License-Identifier: GPL-3.0-only
|
6
|
-
|
7
|
-
# ruff: noqa: N802 D102
|
8
|
-
import datetime
|
9
|
-
import random
|
10
|
-
import sys
|
11
|
-
import unittest
|
12
|
-
from typing import Union
|
13
|
-
|
14
|
-
import adafruit_datetime
|
15
|
-
import uwwvb
|
16
|
-
import zoneinfo
|
17
|
-
|
18
|
-
import wwvb
|
19
|
-
|
20
|
-
EitherDatetimeOrNone = Union[None, datetime.datetime, adafruit_datetime.datetime]
|
21
|
-
|
22
|
-
|
23
|
-
class WWVBRoundtrip(unittest.TestCase):
|
24
|
-
"""tests of uwwvb.py"""
|
25
|
-
|
26
|
-
def assertDateTimeEqualExceptTzInfo(self, a: EitherDatetimeOrNone, b: EitherDatetimeOrNone) -> None:
|
27
|
-
"""Test two datetime objects for equality
|
28
|
-
|
29
|
-
This equality test excludes tzinfo, and allows adafruit_datetime and core datetime modules to compare equal
|
30
|
-
"""
|
31
|
-
assert a
|
32
|
-
assert b
|
33
|
-
self.assertEqual(
|
34
|
-
(a.year, a.month, a.day, a.hour, a.minute, a.second, a.microsecond),
|
35
|
-
(b.year, b.month, b.day, b.hour, b.minute, b.second, b.microsecond),
|
36
|
-
)
|
37
|
-
|
38
|
-
def test_decode(self) -> None:
|
39
|
-
"""Test decoding of some minutes including a leap second.
|
40
|
-
|
41
|
-
Each minute must decode and match the primary decoder.
|
42
|
-
"""
|
43
|
-
minute = wwvb.WWVBMinuteIERS.from_datetime(datetime.datetime(2012, 6, 30, 23, 50, tzinfo=datetime.timezone.utc))
|
44
|
-
assert minute
|
45
|
-
decoder = uwwvb.WWVBDecoder()
|
46
|
-
decoder.update(uwwvb.MARK)
|
47
|
-
any_leap_second = False
|
48
|
-
for _ in range(20):
|
49
|
-
timecode = minute.as_timecode()
|
50
|
-
decoded = None
|
51
|
-
if len(timecode.am) == 61:
|
52
|
-
any_leap_second = True
|
53
|
-
for code in timecode.am:
|
54
|
-
decoded = uwwvb.decode_wwvb(decoder.update(int(code))) or decoded
|
55
|
-
assert decoded
|
56
|
-
self.assertDateTimeEqualExceptTzInfo(
|
57
|
-
minute.as_datetime_utc(),
|
58
|
-
uwwvb.as_datetime_utc(decoded),
|
59
|
-
)
|
60
|
-
minute = minute.next_minute()
|
61
|
-
self.assertTrue(any_leap_second)
|
62
|
-
|
63
|
-
def test_roundtrip(self) -> None:
|
64
|
-
"""Test that some big range of times all decode the same as the primary decoder"""
|
65
|
-
dt = datetime.datetime(2002, 1, 1, 0, 0, tzinfo=datetime.timezone.utc)
|
66
|
-
delta = datetime.timedelta(minutes=7182 if sys.implementation.name == "cpython" else 86400 - 7182)
|
67
|
-
while dt.year < 2013:
|
68
|
-
minute = wwvb.WWVBMinuteIERS.from_datetime(dt)
|
69
|
-
assert minute
|
70
|
-
decoded = uwwvb.decode_wwvb([int(i) for i in minute.as_timecode().am])
|
71
|
-
assert decoded
|
72
|
-
self.assertDateTimeEqualExceptTzInfo(minute.as_datetime_utc(), uwwvb.as_datetime_utc(decoded))
|
73
|
-
dt = dt + delta
|
74
|
-
|
75
|
-
def test_dst(self) -> None:
|
76
|
-
"""Test of DST as handled by the small decoder"""
|
77
|
-
for dt in (
|
78
|
-
datetime.datetime(2021, 3, 14, 8, 59, tzinfo=datetime.timezone.utc),
|
79
|
-
datetime.datetime(2021, 3, 14, 9, 00, tzinfo=datetime.timezone.utc),
|
80
|
-
datetime.datetime(2021, 3, 14, 9, 1, tzinfo=datetime.timezone.utc),
|
81
|
-
datetime.datetime(2021, 3, 15, 8, 59, tzinfo=datetime.timezone.utc),
|
82
|
-
datetime.datetime(2021, 3, 15, 9, 00, tzinfo=datetime.timezone.utc),
|
83
|
-
datetime.datetime(2021, 3, 15, 9, 1, tzinfo=datetime.timezone.utc),
|
84
|
-
datetime.datetime(2021, 11, 7, 8, 59, tzinfo=datetime.timezone.utc),
|
85
|
-
datetime.datetime(2021, 11, 7, 9, 00, tzinfo=datetime.timezone.utc),
|
86
|
-
datetime.datetime(2021, 11, 7, 9, 1, tzinfo=datetime.timezone.utc),
|
87
|
-
datetime.datetime(2021, 11, 8, 8, 59, tzinfo=datetime.timezone.utc),
|
88
|
-
datetime.datetime(2021, 11, 8, 9, 00, tzinfo=datetime.timezone.utc),
|
89
|
-
datetime.datetime(2021, 11, 8, 9, 1, tzinfo=datetime.timezone.utc),
|
90
|
-
datetime.datetime(2021, 7, 7, 9, 1, tzinfo=datetime.timezone.utc),
|
91
|
-
):
|
92
|
-
minute = wwvb.WWVBMinuteIERS.from_datetime(dt)
|
93
|
-
decoded = uwwvb.decode_wwvb([int(i) for i in minute.as_timecode().am])
|
94
|
-
assert decoded
|
95
|
-
self.assertDateTimeEqualExceptTzInfo(minute.as_datetime_local(), uwwvb.as_datetime_local(decoded))
|
96
|
-
|
97
|
-
decoded = uwwvb.decode_wwvb([int(i) for i in minute.as_timecode().am])
|
98
|
-
assert decoded
|
99
|
-
self.assertDateTimeEqualExceptTzInfo(
|
100
|
-
minute.as_datetime_local(dst_observed=False),
|
101
|
-
uwwvb.as_datetime_local(decoded, dst_observed=False),
|
102
|
-
)
|
103
|
-
|
104
|
-
def test_noise(self) -> None:
|
105
|
-
"""Test of the state-machine decoder when faced with pseudorandom noise"""
|
106
|
-
minute = wwvb.WWVBMinuteIERS.from_datetime(
|
107
|
-
datetime.datetime(2012, 6, 30, 23, 50, tzinfo=datetime.timezone.utc),
|
108
|
-
)
|
109
|
-
r = random.Random(408)
|
110
|
-
junk = [
|
111
|
-
r.choice(
|
112
|
-
[
|
113
|
-
wwvb.AmplitudeModulation.MARK,
|
114
|
-
wwvb.AmplitudeModulation.ONE,
|
115
|
-
wwvb.AmplitudeModulation.ZERO,
|
116
|
-
],
|
117
|
-
)
|
118
|
-
for _ in range(480)
|
119
|
-
]
|
120
|
-
timecode = minute.as_timecode()
|
121
|
-
test_input = [*junk, wwvb.AmplitudeModulation.MARK, *timecode.am]
|
122
|
-
decoder = uwwvb.WWVBDecoder()
|
123
|
-
for code in test_input[:-1]:
|
124
|
-
decoded = decoder.update(code)
|
125
|
-
self.assertIsNone(decoded)
|
126
|
-
minute_maybe = decoder.update(wwvb.AmplitudeModulation.MARK)
|
127
|
-
assert minute_maybe
|
128
|
-
decoded_minute = uwwvb.decode_wwvb(minute_maybe)
|
129
|
-
assert decoded_minute
|
130
|
-
self.assertDateTimeEqualExceptTzInfo(
|
131
|
-
minute.as_datetime_utc(),
|
132
|
-
uwwvb.as_datetime_utc(decoded_minute),
|
133
|
-
)
|
134
|
-
self.assertDateTimeEqualExceptTzInfo(
|
135
|
-
minute.as_datetime_local(),
|
136
|
-
uwwvb.as_datetime_local(decoded_minute),
|
137
|
-
)
|
138
|
-
|
139
|
-
def test_noise2(self) -> None:
|
140
|
-
"""Test of the full minute decoder with targeted errors to get full coverage"""
|
141
|
-
minute = wwvb.WWVBMinuteIERS.from_datetime(
|
142
|
-
datetime.datetime(2012, 6, 30, 23, 50, tzinfo=datetime.timezone.utc),
|
143
|
-
)
|
144
|
-
timecode = minute.as_timecode()
|
145
|
-
decoded = uwwvb.decode_wwvb([int(i) for i in timecode.am])
|
146
|
-
self.assertIsNotNone(decoded)
|
147
|
-
for position in uwwvb.always_mark:
|
148
|
-
test_input = [int(i) for i in timecode.am]
|
149
|
-
for noise in (0, 1):
|
150
|
-
test_input[position] = noise
|
151
|
-
decoded = uwwvb.decode_wwvb(test_input)
|
152
|
-
self.assertIsNone(decoded)
|
153
|
-
for position in uwwvb.always_zero:
|
154
|
-
test_input = [int(i) for i in timecode.am]
|
155
|
-
for noise in (1, 2):
|
156
|
-
test_input[position] = noise
|
157
|
-
decoded = uwwvb.decode_wwvb(test_input)
|
158
|
-
self.assertIsNone(decoded)
|
159
|
-
for i in range(8):
|
160
|
-
if i in (0b101, 0b010): # Test the 6 impossible bit-combos
|
161
|
-
continue
|
162
|
-
test_input = [int(i) for i in timecode.am]
|
163
|
-
test_input[36] = i & 1
|
164
|
-
test_input[37] = (i >> 1) & 1
|
165
|
-
test_input[38] = (i >> 2) & 1
|
166
|
-
decoded = uwwvb.decode_wwvb(test_input)
|
167
|
-
self.assertIsNone(decoded)
|
168
|
-
# Invalid year-day
|
169
|
-
test_input = [int(i) for i in timecode.am]
|
170
|
-
test_input[22] = 1
|
171
|
-
test_input[23] = 1
|
172
|
-
test_input[25] = 1
|
173
|
-
decoded = uwwvb.decode_wwvb(test_input)
|
174
|
-
self.assertIsNone(decoded)
|
175
|
-
|
176
|
-
def test_noise3(self) -> None:
|
177
|
-
"""Test impossible BCD values"""
|
178
|
-
minute = wwvb.WWVBMinuteIERS.from_datetime(
|
179
|
-
datetime.datetime(2012, 6, 30, 23, 50, tzinfo=datetime.timezone.utc),
|
180
|
-
)
|
181
|
-
timecode = minute.as_timecode()
|
182
|
-
|
183
|
-
for poslist in [
|
184
|
-
[1, 2, 3, 4], # tens minutes
|
185
|
-
[5, 6, 7, 8], # ones minutes
|
186
|
-
[15, 16, 17, 18], # tens hours
|
187
|
-
[25, 26, 27, 28], # tens days
|
188
|
-
[30, 31, 32, 33], # ones days
|
189
|
-
[40, 41, 42, 43], # tens years
|
190
|
-
[45, 46, 47, 48], # ones years
|
191
|
-
[50, 51, 52, 53], # ones dut1
|
192
|
-
]:
|
193
|
-
with self.subTest(test=poslist):
|
194
|
-
test_input = [int(i) for i in timecode.am]
|
195
|
-
for pi in poslist:
|
196
|
-
test_input[pi] = 1
|
197
|
-
decoded = uwwvb.decode_wwvb(test_input)
|
198
|
-
self.assertIsNone(decoded)
|
199
|
-
|
200
|
-
def test_str(self) -> None:
|
201
|
-
"""Test the str() of a WWVBDecoder"""
|
202
|
-
self.assertEqual(str(uwwvb.WWVBDecoder()), "<WWVBDecoder 1 []>")
|
203
|
-
|
204
|
-
def test_near_year_bug(self) -> None:
|
205
|
-
"""Test for a bug seen in another WWVB implementaiton
|
206
|
-
|
207
|
-
.. in which the hours after UTC midnight on 12-31 of a leap year would
|
208
|
-
be shown incorrectly. Check that we don't have that bug.
|
209
|
-
"""
|
210
|
-
minute = wwvb.WWVBMinuteIERS.from_datetime(datetime.datetime(2021, 1, 1, 0, 0, tzinfo=datetime.timezone.utc))
|
211
|
-
timecode = minute.as_timecode()
|
212
|
-
decoded = uwwvb.decode_wwvb([int(i) for i in timecode.am])
|
213
|
-
assert decoded
|
214
|
-
self.assertDateTimeEqualExceptTzInfo(
|
215
|
-
datetime.datetime(2020, 12, 31, 17, 00, tzinfo=zoneinfo.ZoneInfo("America/Denver")), # Mountain time!
|
216
|
-
uwwvb.as_datetime_local(decoded),
|
217
|
-
)
|
218
|
-
|
219
|
-
|
220
|
-
if __name__ == "__main__": # pragma no cover
|
221
|
-
unittest.main()
|