wwvb 3.0.7__py3-none-any.whl → 4.0.0__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.
- uwwvb.py +5 -2
- wwvb/__init__.py +212 -179
- wwvb/__version__.py +2 -2
- wwvb/decode.py +8 -9
- wwvb/gen.py +14 -16
- wwvb/iersdata.py +4 -5
- wwvb/iersdata_dist.py +4 -6
- wwvb/testcli.py +8 -5
- wwvb/testpm.py +2 -2
- wwvb/testuwwvb.py +42 -31
- wwvb/testwwvb.py +51 -40
- wwvb/updateiers.py +35 -33
- wwvb/wwvbtk.py +13 -13
- {wwvb-3.0.7.dist-info → wwvb-4.0.0.dist-info}/METADATA +1 -1
- wwvb-4.0.0.dist-info/RECORD +23 -0
- {wwvb-3.0.7.dist-info → wwvb-4.0.0.dist-info}/WHEEL +1 -1
- wwvb-3.0.7.dist-info/RECORD +0 -23
- {wwvb-3.0.7.dist-info → wwvb-4.0.0.dist-info}/entry_points.txt +0 -0
- {wwvb-3.0.7.dist-info → wwvb-4.0.0.dist-info}/top_level.txt +0 -0
wwvb/updateiers.py
CHANGED
@@ -6,32 +6,34 @@
|
|
6
6
|
|
7
7
|
"""Update the DUT1 and LS data based on online sources"""
|
8
8
|
|
9
|
+
from __future__ import annotations
|
10
|
+
|
9
11
|
import csv
|
10
12
|
import datetime
|
11
13
|
import io
|
12
14
|
import itertools
|
13
|
-
import os
|
14
15
|
import pathlib
|
15
|
-
from typing import Callable
|
16
|
+
from typing import Callable
|
16
17
|
|
17
18
|
import bs4
|
18
19
|
import click
|
19
20
|
import platformdirs
|
20
21
|
import requests
|
21
22
|
|
22
|
-
DIST_PATH =
|
23
|
+
DIST_PATH = pathlib.Path(__file__).parent / "iersdata_dist.py"
|
23
24
|
|
24
|
-
OLD_TABLE_START:
|
25
|
-
OLD_TABLE_END:
|
26
|
-
if
|
25
|
+
OLD_TABLE_START: datetime.date | None = None
|
26
|
+
OLD_TABLE_END: datetime.date | None = None
|
27
|
+
if DIST_PATH.exists():
|
27
28
|
import wwvb.iersdata_dist
|
28
29
|
|
29
30
|
OLD_TABLE_START = wwvb.iersdata_dist.DUT1_DATA_START
|
30
31
|
OLD_TABLE_END = OLD_TABLE_START + datetime.timedelta(days=len(wwvb.iersdata_dist.DUT1_OFFSETS) - 1)
|
31
32
|
|
32
33
|
IERS_URL = "https://datacenter.iers.org/data/csv/finals2000A.all.csv"
|
33
|
-
|
34
|
-
|
34
|
+
IERS_PATH = pathlib.Path("finals2000A.all.csv")
|
35
|
+
if IERS_PATH.exists():
|
36
|
+
IERS_URL = str(IERS_PATH)
|
35
37
|
print("using local", IERS_URL)
|
36
38
|
NIST_URL = "https://www.nist.gov/pml/time-and-frequency-division/atomic-standards/leap-second-and-ut1-utc-information"
|
37
39
|
|
@@ -42,15 +44,14 @@ def _get_text(url: str) -> str:
|
|
42
44
|
with requests.get(url, timeout=30) as response:
|
43
45
|
return response.text
|
44
46
|
else:
|
45
|
-
return
|
47
|
+
return pathlib.Path(url).read_text(encoding="utf-8")
|
46
48
|
|
47
49
|
|
48
|
-
def update_iersdata( #
|
49
|
-
|
50
|
+
def update_iersdata( # noqa: PLR0915, PLR0912
|
51
|
+
target_path: pathlib.Path,
|
50
52
|
) -> None:
|
51
53
|
"""Update iersdata.py"""
|
52
|
-
|
53
|
-
offsets: List[int] = []
|
54
|
+
offsets: list[int] = []
|
54
55
|
iersdata_text = _get_text(IERS_URL)
|
55
56
|
for r in csv.DictReader(io.StringIO(iersdata_text), delimiter=";"):
|
56
57
|
jd = float(r["MJD"])
|
@@ -97,11 +98,11 @@ def update_iersdata( # pylint: disable=too-many-locals, too-many-branches, too-
|
|
97
98
|
off_end = (patch_end - table_start).days
|
98
99
|
offsets[off_start:off_end] = [val] * (off_end - off_start)
|
99
100
|
|
100
|
-
wwvb_dut1:
|
101
|
-
wwvb_start:
|
101
|
+
wwvb_dut1: int | None = None
|
102
|
+
wwvb_start: datetime.date | None = None
|
102
103
|
for row in wwvb_dut1_table.findAll("tr")[1:][::-1]:
|
103
104
|
cells = row.findAll("td")
|
104
|
-
when = datetime.datetime.strptime(cells[0].text, "%Y-%m-%d").date()
|
105
|
+
when = datetime.datetime.strptime(cells[0].text + "+0000", "%Y-%m-%d%z").date()
|
105
106
|
dut1 = cells[2].text.replace("s", "").replace(" ", "")
|
106
107
|
dut1 = int(round(float(dut1) * 10))
|
107
108
|
if wwvb_dut1 is not None:
|
@@ -123,25 +124,24 @@ def update_iersdata( # pylint: disable=too-many-locals, too-many-branches, too-
|
|
123
124
|
assert wwvb_start is not None
|
124
125
|
patch(wwvb_start, wwvb_data_stamp + datetime.timedelta(days=1), wwvb_dut1)
|
125
126
|
|
126
|
-
with open(
|
127
|
+
with target_path.open("w", encoding="utf-8") as output:
|
127
128
|
|
128
129
|
def code(*args: str) -> None:
|
129
130
|
"""Print to the output file"""
|
130
131
|
print(*args, file=output)
|
131
132
|
|
132
133
|
code("# -*- python3 -*-")
|
134
|
+
code("# fmt: off")
|
133
135
|
code('"""File generated from public data - not subject to copyright"""')
|
134
136
|
code("# SPDX" + "-FileCopyrightText: Public domain")
|
135
137
|
code("# SPDX" + "-License-Identifier: CC0-1.0")
|
136
|
-
code("# fmt: off")
|
137
138
|
code("# isort: skip_file")
|
138
|
-
code("# pylint: disable=invalid-name")
|
139
139
|
code("import datetime")
|
140
140
|
|
141
141
|
code("__all__ = ['DUT1_DATA_START', 'DUT1_OFFSETS']")
|
142
|
-
code(f"DUT1_DATA_START = {
|
142
|
+
code(f"DUT1_DATA_START = {table_start!r}")
|
143
143
|
c = sorted(chr(ord("a") + ch + 10) for ch in set(offsets))
|
144
|
-
code(f"{','.join(c)} = tuple({
|
144
|
+
code(f"{','.join(c)} = tuple({''.join(c)!r})")
|
145
145
|
code(f"DUT1_OFFSETS = str( # {table_start.year:04d}{table_start.month:02d}{table_start.day:02d}")
|
146
146
|
line = ""
|
147
147
|
j = 0
|
@@ -152,10 +152,7 @@ def update_iersdata( # pylint: disable=too-many-locals, too-many-branches, too-
|
|
152
152
|
sz = len(list(it))
|
153
153
|
if j:
|
154
154
|
part = part + "+"
|
155
|
-
if sz < 2
|
156
|
-
part = part + ch
|
157
|
-
else:
|
158
|
-
part = part + f"{ch}*{sz}"
|
155
|
+
part = part + ch if sz < 2 else part + f"{ch}*{sz}"
|
159
156
|
j += sz
|
160
157
|
if len(line + part) > 60:
|
161
158
|
d = table_start + datetime.timedelta(j - 1)
|
@@ -172,25 +169,30 @@ def update_iersdata( # pylint: disable=too-many-locals, too-many-branches, too-
|
|
172
169
|
print(f"iersdata covers {table_start} .. {table_end}")
|
173
170
|
|
174
171
|
|
175
|
-
def iersdata_path(callback: Callable[[str, str],
|
172
|
+
def iersdata_path(callback: Callable[[str, str], pathlib.Path]) -> pathlib.Path:
|
176
173
|
"""Find out the path for this directory"""
|
177
|
-
|
174
|
+
print("iersdata_path", callback)
|
175
|
+
r = callback("wwvbpy", "unpythonic.net") / "wwvb_iersdata.py"
|
176
|
+
print(f"iersdata_path {r=!r}")
|
177
|
+
return r
|
178
178
|
|
179
179
|
|
180
180
|
@click.command()
|
181
181
|
@click.option(
|
182
182
|
"--user",
|
183
183
|
"location",
|
184
|
-
flag_value=iersdata_path(platformdirs.
|
185
|
-
default=iersdata_path(platformdirs.
|
184
|
+
flag_value=iersdata_path(platformdirs.user_data_path),
|
185
|
+
default=iersdata_path(platformdirs.user_data_path),
|
186
|
+
type=pathlib.Path,
|
186
187
|
)
|
187
188
|
@click.option("--dist", "location", flag_value=DIST_PATH)
|
188
|
-
@click.option("--site", "location", flag_value=iersdata_path(platformdirs.
|
189
|
+
@click.option("--site", "location", flag_value=iersdata_path(platformdirs.site_data_path))
|
189
190
|
def main(location: str) -> None:
|
190
191
|
"""Update DUT1 data"""
|
191
|
-
|
192
|
-
|
193
|
-
|
192
|
+
path = pathlib.Path(location)
|
193
|
+
print(f"will write to {location!r}")
|
194
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
195
|
+
update_iersdata(path)
|
194
196
|
|
195
197
|
|
196
198
|
if __name__ == "__main__":
|
wwvb/wwvbtk.py
CHANGED
@@ -5,12 +5,13 @@
|
|
5
5
|
# SPDX-FileCopyrightText: 2021 Jeff Epler
|
6
6
|
#
|
7
7
|
# SPDX-License-Identifier: GPL-3.0-only
|
8
|
+
from __future__ import annotations
|
8
9
|
|
9
10
|
import functools
|
10
11
|
import threading
|
11
12
|
import time
|
12
|
-
from tkinter import Canvas, TclError, Tk
|
13
|
-
from typing import Any, Generator
|
13
|
+
from tkinter import Canvas, TclError, Tk
|
14
|
+
from typing import Any, Generator
|
14
15
|
|
15
16
|
import click
|
16
17
|
|
@@ -23,9 +24,7 @@ def _app() -> Tk:
|
|
23
24
|
return Tk()
|
24
25
|
|
25
26
|
|
26
|
-
def validate_colors( #
|
27
|
-
ctx: Any, param: Any, value: str
|
28
|
-
) -> list[str]:
|
27
|
+
def validate_colors(ctx: Any, param: Any, value: str) -> list[str]: # noqa: ARG001
|
29
28
|
"""Check that all colors in a string are valid, splitting it to a list"""
|
30
29
|
app = _app()
|
31
30
|
colors = value.split()
|
@@ -55,9 +54,8 @@ DEFAULT_COLORS = "#3c3c3c #3c3c3c #3c3c3c #cc3c3c #88883c #3ccc3c"
|
|
55
54
|
@click.option("--colors", callback=validate_colors, default=DEFAULT_COLORS)
|
56
55
|
@click.option("--size", default=48)
|
57
56
|
@click.option("--min-size", default=None)
|
58
|
-
def main(colors: list[str], size: int, min_size:
|
57
|
+
def main(colors: list[str], size: int, min_size: int | None) -> None: # noqa: PLR0915
|
59
58
|
"""Visualize the WWVB signal in realtime"""
|
60
|
-
|
61
59
|
if min_size is None:
|
62
60
|
min_size = size
|
63
61
|
|
@@ -67,7 +65,7 @@ def main(colors: list[str], size: int, min_size: Optional[int]) -> None:
|
|
67
65
|
if deadline > now:
|
68
66
|
time.sleep(deadline - now)
|
69
67
|
|
70
|
-
def wwvbtick() -> Generator[
|
68
|
+
def wwvbtick() -> Generator[tuple[float, wwvb.AmplitudeModulation], None, None]:
|
71
69
|
"""Yield consecutive values of the WWVB amplitude signal, going from minute to minute"""
|
72
70
|
timestamp = time.time() // 60 * 60
|
73
71
|
|
@@ -79,13 +77,15 @@ def main(colors: list[str], size: int, min_size: Optional[int]) -> None:
|
|
79
77
|
yield timestamp + i, code
|
80
78
|
timestamp = timestamp + 60
|
81
79
|
|
82
|
-
def wwvbsmarttick() -> Generator[
|
83
|
-
"""Yield consecutive values of the WWVB amplitude signal
|
84
|
-
|
85
|
-
|
80
|
+
def wwvbsmarttick() -> Generator[tuple[float, wwvb.AmplitudeModulation], None, None]:
|
81
|
+
"""Yield consecutive values of the WWVB amplitude signal
|
82
|
+
|
83
|
+
.. but deal with time progressing unexpectedly, such as when the
|
84
|
+
computer is suspended or NTP steps the clock backwards
|
86
85
|
|
87
86
|
When time goes backwards or advances by more than a minute, get a fresh
|
88
|
-
wwvbtick object; otherwise, discard time signals more than 1s in the past.
|
87
|
+
wwvbtick object; otherwise, discard time signals more than 1s in the past.
|
88
|
+
"""
|
89
89
|
while True:
|
90
90
|
for stamp, code in wwvbtick():
|
91
91
|
now = time.time()
|
@@ -0,0 +1,23 @@
|
|
1
|
+
uwwvb.py,sha256=Ocys0AK10NocugDxNjIjUkL3_uVqD2Q6c2WLfqXmx1s,5732
|
2
|
+
wwvb/__init__.py,sha256=gSeXqukkcHnYCJAkbXk4eWBMqBu7n3GJ7CJM32XMiaU,30770
|
3
|
+
wwvb/__version__.py,sha256=TgVqVkMXXQVomuTpZfj8uxnyooVWsiw-3pM8cC2qwwE,411
|
4
|
+
wwvb/decode.py,sha256=d7bAG2pVsFMX0uxY6pMaz-psdbLXauSHh55JDaUT6W4,2713
|
5
|
+
wwvb/dut1table.py,sha256=uqaCnCOWr7ytx-nt3mmyhFb9jJVNP5N-WJX-glunKAk,890
|
6
|
+
wwvb/gen.py,sha256=0hgicarLgCtOgBgIpb9sU08yzKgCGuwIYOqblR34H5I,3791
|
7
|
+
wwvb/iersdata.py,sha256=NOItawXnXwd3Gq3K_cx85NVQ_iogLw3MDgzOI8q_75Y,924
|
8
|
+
wwvb/iersdata_dist.py,sha256=zQVO2AjiBTrhpSs21HUG9fL4S11K3WzTk0kP_eyMXz4,2364
|
9
|
+
wwvb/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
10
|
+
wwvb/testcli.py,sha256=wa1E9iRaE62OBHshR4K_hxhIzSNxwpWy8rgoEKuhrqs,10168
|
11
|
+
wwvb/testdaylight.py,sha256=JW8UJK-FeAg9Kjy5F_aBYbUVj44DKpJOXQ-u9ooyprA,2485
|
12
|
+
wwvb/testls.py,sha256=Kz4-MWLaUKABwyql8sWdzvtg8gipxhHv4r-6fn3fptg,1720
|
13
|
+
wwvb/testpm.py,sha256=JR3V_EIm0Su3c4m5CcKpMANL3aZnpzEzDL3KhgKX3rM,905
|
14
|
+
wwvb/testuwwvb.py,sha256=SMvq85tJgjGMYHKsreCtlM4Wrb3MYOOjf5rKf9wuHYc,9176
|
15
|
+
wwvb/testwwvb.py,sha256=t0TUJhHOh-mRFD-GUvePzHs7D3DkNb_paRAI1uYH8l8,16807
|
16
|
+
wwvb/tz.py,sha256=XVYh0btrnyP_nUiZUwBjufkYbb26_DTiZVl-R_1BA2A,299
|
17
|
+
wwvb/updateiers.py,sha256=3Bg0cpQnTDrGWcHbi0rwZ4rSURJcO64Bds9FDTYLSK8,7382
|
18
|
+
wwvb/wwvbtk.py,sha256=WumGtKsAxMw1HMTfm5x4dOKmHikCaDBRZfh_WbF4iu0,4585
|
19
|
+
wwvb-4.0.0.dist-info/METADATA,sha256=w5zC4RwrShajTmBzJfDUFhWpG8SzoiucJochmO45ZUM,10290
|
20
|
+
wwvb-4.0.0.dist-info/WHEEL,sha256=Z4pYXqR_rTB7OWNDYFOm1qRk0RX6GFP2o8LgvP453Hk,91
|
21
|
+
wwvb-4.0.0.dist-info/entry_points.txt,sha256=KSevvHWLEKxOxUQ-L-OQidD4Sj2BPEfhZ2TQhOgyys4,179
|
22
|
+
wwvb-4.0.0.dist-info/top_level.txt,sha256=0IYdkhEAMgurpv_F-76rlyn4GdxepGFzG99tivVdQVU,11
|
23
|
+
wwvb-4.0.0.dist-info/RECORD,,
|
wwvb-3.0.7.dist-info/RECORD
DELETED
@@ -1,23 +0,0 @@
|
|
1
|
-
uwwvb.py,sha256=zX0pkBvo-Nn1QNI2NWvCE0sH6dlSgz8IHoszhgMf8XI,5720
|
2
|
-
wwvb/__init__.py,sha256=sRUn_0A7FMqbTcq2k3sX_13FS3gB3A1GOvr0vyYWDzo,30100
|
3
|
-
wwvb/__version__.py,sha256=LJ4HJPpXI_UsV9wSih_sOZxstGnvrh7nUYp4E-ZbpXc,411
|
4
|
-
wwvb/decode.py,sha256=PdRKvSVzyWwMyaM6BGkwXm7RqDW5EByw7NBcIpkNiqY,2795
|
5
|
-
wwvb/dut1table.py,sha256=uqaCnCOWr7ytx-nt3mmyhFb9jJVNP5N-WJX-glunKAk,890
|
6
|
-
wwvb/gen.py,sha256=v5n23peFc4wOuunAmAe0NogTQIttp5F4KOSwJMRqUlc,3792
|
7
|
-
wwvb/iersdata.py,sha256=bMkIY5IAd0n93EQe16BoCqtDkXjnqkjG5L20F3Egp4o,994
|
8
|
-
wwvb/iersdata_dist.py,sha256=O4sDlkJPeIcX8pSzrxl_5RptOdC_F8e_KxyV3JIEHjQ,2395
|
9
|
-
wwvb/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
10
|
-
wwvb/testcli.py,sha256=8Mgn6l7qddeudtZmZNCKgPvVJYoWBlQupuIdyvQlNtQ,10118
|
11
|
-
wwvb/testdaylight.py,sha256=JW8UJK-FeAg9Kjy5F_aBYbUVj44DKpJOXQ-u9ooyprA,2485
|
12
|
-
wwvb/testls.py,sha256=Kz4-MWLaUKABwyql8sWdzvtg8gipxhHv4r-6fn3fptg,1720
|
13
|
-
wwvb/testpm.py,sha256=I5ajzZjUypyKszSe4sTnMZbS43nPmee4H1G3uZlHC_w,935
|
14
|
-
wwvb/testuwwvb.py,sha256=kGINMTDNuA5TxBnFok-eamnlcxFXxjVuhvgELums31k,8444
|
15
|
-
wwvb/testwwvb.py,sha256=Sl5s01OqDv8mEZsXocII24hj9CinBlphJCUFtAgXvE4,16143
|
16
|
-
wwvb/tz.py,sha256=XVYh0btrnyP_nUiZUwBjufkYbb26_DTiZVl-R_1BA2A,299
|
17
|
-
wwvb/updateiers.py,sha256=_yaMeaW4pWSkDl5b8Dur-gdoQVGode6bVjH1kvYvagU,7362
|
18
|
-
wwvb/wwvbtk.py,sha256=CdzgUp6AbwMvSi62fJBDUlRDyApfVsE3qKL7FucBqno,4598
|
19
|
-
wwvb-3.0.7.dist-info/METADATA,sha256=y2SJcCnjkd4htfzY7sL7OPCHQCmL2NwJ6fkuBGqnKjY,10290
|
20
|
-
wwvb-3.0.7.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
21
|
-
wwvb-3.0.7.dist-info/entry_points.txt,sha256=KSevvHWLEKxOxUQ-L-OQidD4Sj2BPEfhZ2TQhOgyys4,179
|
22
|
-
wwvb-3.0.7.dist-info/top_level.txt,sha256=0IYdkhEAMgurpv_F-76rlyn4GdxepGFzG99tivVdQVU,11
|
23
|
-
wwvb-3.0.7.dist-info/RECORD,,
|
File without changes
|
File without changes
|