wwvb 4.0.0__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.
wwvb/wwvbtk.py DELETED
@@ -1,144 +0,0 @@
1
- #!/usr/bin/python3
2
- """Visualize the WWVB signal in realtime"""
3
-
4
- # Copyright (C) 2011-2020 Jeff Epler <jepler@gmail.com>
5
- # SPDX-FileCopyrightText: 2021 Jeff Epler
6
- #
7
- # SPDX-License-Identifier: GPL-3.0-only
8
- from __future__ import annotations
9
-
10
- import functools
11
- import threading
12
- import time
13
- from tkinter import Canvas, TclError, Tk
14
- from typing import Any, Generator
15
-
16
- import click
17
-
18
- import wwvb
19
-
20
-
21
- @functools.cache
22
- def _app() -> Tk:
23
- """Create the Tk application object lazily"""
24
- return Tk()
25
-
26
-
27
- def validate_colors(ctx: Any, param: Any, value: str) -> list[str]: # noqa: ARG001
28
- """Check that all colors in a string are valid, splitting it to a list"""
29
- app = _app()
30
- colors = value.split()
31
- if len(colors) not in (2, 3, 4, 6):
32
- raise click.BadParameter(f"Give 2, 3, 4 or 6 colors (not {len(colors)}")
33
- for c in colors:
34
- try:
35
- app.winfo_rgb(c)
36
- except TclError as e:
37
- raise click.BadParameter(f"Invalid color {c}") from e
38
-
39
- if len(colors) == 2:
40
- off, on = colors
41
- return [off, off, off, on, on, on]
42
- if len(colors) == 3:
43
- return colors + colors
44
- if len(colors) == 4:
45
- off, c1, c2, c3 = colors
46
- return [off, off, off, c1, c2, c3]
47
- return colors
48
-
49
-
50
- DEFAULT_COLORS = "#3c3c3c #3c3c3c #3c3c3c #cc3c3c #88883c #3ccc3c"
51
-
52
-
53
- @click.command
54
- @click.option("--colors", callback=validate_colors, default=DEFAULT_COLORS)
55
- @click.option("--size", default=48)
56
- @click.option("--min-size", default=None)
57
- def main(colors: list[str], size: int, min_size: int | None) -> None: # noqa: PLR0915
58
- """Visualize the WWVB signal in realtime"""
59
- if min_size is None:
60
- min_size = size
61
-
62
- def sleep_deadline(deadline: float) -> None:
63
- """Sleep until a deadline"""
64
- now = time.time()
65
- if deadline > now:
66
- time.sleep(deadline - now)
67
-
68
- def wwvbtick() -> Generator[tuple[float, wwvb.AmplitudeModulation], None, None]:
69
- """Yield consecutive values of the WWVB amplitude signal, going from minute to minute"""
70
- timestamp = time.time() // 60 * 60
71
-
72
- while True:
73
- tt = time.gmtime(timestamp)
74
- key = tt.tm_year, tt.tm_yday, tt.tm_hour, tt.tm_min
75
- timecode = wwvb.WWVBMinuteIERS(*key).as_timecode()
76
- for i, code in enumerate(timecode.am):
77
- yield timestamp + i, code
78
- timestamp = timestamp + 60
79
-
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
85
-
86
- When time goes backwards or advances by more than a minute, get a fresh
87
- wwvbtick object; otherwise, discard time signals more than 1s in the past.
88
- """
89
- while True:
90
- for stamp, code in wwvbtick():
91
- now = time.time()
92
- if stamp < now - 60:
93
- break
94
- if stamp < now - 1:
95
- continue
96
- yield stamp, code
97
-
98
- app = _app()
99
- app.wm_minsize(min_size, min_size)
100
- canvas = Canvas(app, width=size, height=size, highlightthickness=0)
101
- circle = canvas.create_oval(4, 4, 44, 44, outline="black", fill=colors[0])
102
- canvas.pack(fill="both", expand=True)
103
- app.wm_deiconify()
104
-
105
- def resize_canvas(event: Any) -> None:
106
- """Keep the circle filling the window when it is resized"""
107
- sz = min(event.width, event.height) - 8
108
- if sz < 0:
109
- return
110
- canvas.coords(
111
- circle,
112
- event.width // 2 - sz // 2,
113
- event.height // 2 - sz // 2,
114
- event.width // 2 + sz // 2,
115
- event.height // 2 + sz // 2,
116
- )
117
-
118
- canvas.bind("<Configure>", resize_canvas)
119
-
120
- def led_on(i: int) -> None:
121
- """Turn the canvas's virtual LED on"""
122
- canvas.itemconfigure(circle, fill=colors[i + 3])
123
-
124
- def led_off(i: int) -> None:
125
- """Turn the canvas's virtual LED off"""
126
- canvas.itemconfigure(circle, fill=colors[i])
127
-
128
- def thread_func() -> None:
129
- """Update the canvas virtual LED"""
130
- for stamp, code in wwvbsmarttick():
131
- sleep_deadline(stamp)
132
- led_on(code)
133
- app.update()
134
- sleep_deadline(stamp + 0.2 + 0.3 * int(code))
135
- led_off(code)
136
- app.update()
137
-
138
- thread = threading.Thread(target=thread_func, daemon=True)
139
- thread.start()
140
- app.mainloop()
141
-
142
-
143
- if __name__ == "__main__":
144
- main()
@@ -1,199 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: wwvb
3
- Version: 4.0.0
4
- Summary: Generate WWVB timecodes for any desired time
5
- Home-page: https://github.com/jepler/wwvbpy
6
- Author: Jeff Epler
7
- Author-email: jepler@gmail.com
8
- Classifier: Programming Language :: Python :: 3
9
- Classifier: Programming Language :: Python :: 3.9
10
- Classifier: Programming Language :: Python :: 3.10
11
- Classifier: Programming Language :: Python :: 3.11
12
- Classifier: Programming Language :: Python :: Implementation :: PyPy
13
- Classifier: Programming Language :: Python :: Implementation :: CPython
14
- Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
15
- Classifier: Operating System :: OS Independent
16
- Requires-Python: >=3.9
17
- Description-Content-Type: text/markdown
18
- Requires-Dist: adafruit-circuitpython-datetime
19
- Requires-Dist: beautifulsoup4
20
- Requires-Dist: click
21
- Requires-Dist: leapseconddata
22
- Requires-Dist: platformdirs
23
- Requires-Dist: python-dateutil
24
- Requires-Dist: requests
25
-
26
- <!--
27
- SPDX-FileCopyrightText: 2021 Jeff Epler
28
-
29
- SPDX-License-Identifier: GPL-3.0-only
30
- -->
31
- [![Test wwvbgen](https://github.com/jepler/wwvbpy/actions/workflows/test.yml/badge.svg)](https://github.com/jepler/wwvbpy/actions/workflows/test.yml)
32
- [![codecov](https://codecov.io/gh/jepler/wwvbpy/branch/main/graph/badge.svg?token=Exx0c3Gp65)](https://codecov.io/gh/jepler/wwvbpy)
33
- [![Update DUT1 data](https://github.com/jepler/wwvbpy/actions/workflows/cron.yml/badge.svg)](https://github.com/jepler/wwvbpy/actions/workflows/cron.yml)
34
- [![PyPI](https://img.shields.io/pypi/v/wwvb)](https://pypi.org/project/wwvb)
35
- [![CodeQL](https://github.com/jepler/wwvbpy/actions/workflows/codeql.yml/badge.svg)](https://github.com/jepler/wwvbpy/actions/workflows/codeql.yml)
36
- [![pre-commit.ci status](https://results.pre-commit.ci/badge/github/jepler/wwvbpy/main.svg)](https://results.pre-commit.ci/latest/github/jepler/wwvbpy/main)
37
-
38
- # Purpose
39
-
40
- wwvbpy generates WWVB timecodes for any desired time. These timecodes
41
- may be useful in testing WWVB decoder software.
42
-
43
- Where possible, wwvbpy uses existing facilities for calendar and time
44
- manipulation (datetime and dateutil).
45
-
46
- It uses DUT1/leap second data derived from IERS Bulletin "A" and from NIST's
47
- "Leap second and UT1-UTC information" page. With regular updates to
48
- the "iersdata", wwvbpy should be able to correctly encode the time anywhere
49
- within the 100-year WWVB epoch. (yes, WWVB uses a 2-digit year! In order to
50
- work with historical data, the epoch is arbitrarily assumed to run from 1970 to
51
- 2069.)
52
-
53
- Programs include:
54
- * `wwvbgen`, the main commandline generator program
55
- * `wwvbdecode`, the main commandline decoder program
56
- * `wwvbtk`, visualize the simulated WWVB signal in real-time using Tkinter
57
- * `dut1table`, print the full history of dut1 values, including estimated future values
58
- * `updateiers`, download the latest dut1 data including prospective data from IERS and NIST
59
-
60
- The package includes:
61
- * `wwvb`, for generating WWVB timecodes
62
- * `wwvb.decode`, a generator-based state machine for decoding WWVB timecodes (amplitude modulation only)
63
- * `uwwvb`, a version of the decoder intended for use on constrained environments such as [CircuitPython](https://circuitpython.org).
64
-
65
- # Development status
66
-
67
- The author (@jepler) occasionally develops and maintains this project, but
68
- issues are not likely to be acted on. They would be interested in adding
69
- co-maintainer(s).
70
-
71
-
72
- # WWVB Timecodes
73
-
74
- The National Institute of Standards and Technology operates the WWVB time
75
- signal service near Fort Collins, Colorado. The signal can be received in most
76
- of the continental US. Each minute, the signal transmits the current time,
77
- including information about leap years, daylight saving time, and leap seconds.
78
- The signal is composed of an amplitude channel and a phase modulation channel.
79
-
80
- The amplitude channel can be visualized as a sequence of (usually) 60 symbols,
81
- which by default wwvbgen displays as 0, 1, or 2. The 0s and 1s encode
82
- information like the current day of the year, while the 2s appear in fixed
83
- locations to allow a receiver to determine the start of a minute.
84
-
85
- The phase channel (which is displayed with `--channel=phase` or
86
- `--channel=both`) consists of the same number of symbols per minute. This
87
- channel is substantially more complicated than the phase channel. It encodes
88
- the current time as minute-of-the-century, provides extended DST information,
89
- and includes error-correction information not available in the amplitude
90
- channel.
91
-
92
- # Usage
93
-
94
- ~~~~
95
- Usage: python -m wwvb.gen [OPTIONS] [TIMESPEC]...
96
-
97
- Generate WWVB timecodes
98
-
99
- TIMESPEC: one of "year yday hour minute" or "year month day hour minute", or
100
- else the current minute
101
-
102
- Options:
103
- -i, --iers / -I, --no-iers Whether to use IESR data for DUT1 and LS.
104
- (Default: --iers)
105
- -s, --leap-second Force a positive leap second at the end of
106
- the GMT month (Implies --no-iers)
107
- -n, --negative-leap-second Force a negative leap second at the end of
108
- the GMT month (Implies --no-iers)
109
- -S, --no-leap-second Force no leap second at the end of the month
110
- (Implies --no-iers)
111
- -d, --dut1 INTEGER Force the DUT1 value (Implies --no-iers)
112
- -m, --minutes INTEGER Number of minutes to show (default: 10)
113
- --style [bar|cradek|default|duration|json|sextant]
114
- Style of output
115
- -t, --all-timecodes / -T, --no-all-timecodes
116
- Show the 'WWVB timecode' line before each
117
- minute
118
- --channel [amplitude|phase|both]
119
- Modulation to show (default: amplitude)
120
- --help Show this message and exit.
121
- ~~~~
122
-
123
- For example, to display the leap second that occurred at the end of 1998,
124
- ~~~~
125
- $ python wwvbgen.py -m 7 1998 365 23 56
126
- WWVB timecode: year=98 days=365 hour=23 min=56 dst=0 ut1=-300 ly=0 ls=1
127
- '98+365 23:56 210100110200100001120011001102010100010200110100121000001002
128
- '98+365 23:57 210100111200100001120011001102010100010200110100121000001002
129
- '98+365 23:58 210101000200100001120011001102010100010200110100121000001002
130
- '98+365 23:59 2101010012001000011200110011020101000102001101001210000010022
131
- '99+001 00:00 200000000200000000020000000002000100101201110100121001000002
132
- '99+001 00:01 200000001200000000020000000002000100101201110100121001000002
133
- '99+001 00:02 200000010200000000020000000002000100101201110100121001000002
134
- ~~~~
135
- (the leap second is the extra digit at the end of the 23:59 line; that minute
136
- consists of 61 seconds, instead of the normal 60)
137
-
138
-
139
- # How wwvbpy handles DUT1 data
140
-
141
- wwvbpy stores a compact representation of DUT1 values in `wwvb/iersdata_dist.py` or `wwvb_iersdata.py`.
142
- In this representation, one value is used for one day (0000UTC through 2359UTC).
143
- The letters `a` through `u` represent offsets of -1.0s through +1.0s
144
- in 0.1s increments; `k` represents 0s. (In practice, only a smaller range
145
- of values, typically -0.7s to +0.8s, is seen)
146
-
147
- For 2001 through 2019, NIST has published the actual DUT1 values broadcast,
148
- and the date of each change, though it in the format of an HTML
149
- table and not designed for machine readability:
150
-
151
- https://www.nist.gov/pml/time-and-frequency-division/atomic-standards/leap-second-and-ut1-utc-information
152
-
153
- NIST does not update the value daily and does not seem to follow any
154
- specific rounding rule. Rather, in WWVB "the resolution of the DUT1
155
- correction is 0.1 s, and represents an average value for an extended
156
- range of dates. Therefore, it will not agree exactly with the weekly
157
- UT1-UTC(NIST) values shown in the earlier table, which have 1 ms
158
- resolution and are updated weekly." Like wwvbpy's compact
159
- representation of DUT1 values, the real WWVB does not appear to ever
160
- broadcast DUT1=-0.0.
161
-
162
- For a larger range of dates spanning 1973 through approximately one year from
163
- now, IERS publishes historical and prospective UT1-UTC values to multiple
164
- decimal places, in a machine readable fixed length format.
165
-
166
- wwvbpy merges the WWVB and IERS datasets, favoring the WWVB dataset for dates when it is available. There are some caveats to this, which are mostly commented in the `wwvb/updateiers.py` script.
167
-
168
- `wwvb/iersdata_dist.py` is updated monthly from github actions or with `iersdata --dist` from within the wwvbpy source tree. However, at this time, releases are not regularly made from the updated information.
169
-
170
- A site or user version of the file, `wwvb_iersdata.py` can be created or updated with `iersdata --site` or `iersdata --user`. If the distributed iersdata is out of date, the generator will prompt you to run the update command.
171
-
172
- Leap seconds are inferred from the DUT1 data as follows: If X and Y are the
173
- 1-digit-rounded DUT1 values for consecutive dates, and `X*Y<0`, then there is a
174
- leap second at the end of day X. The direction of the leap second can be
175
- inferred from the sign of X, a 59-second minute if X is positive and a
176
- 61-second minute if it is negative. As long
177
- as DUT1 changes slowly enough during other times that there is at least one day
178
- of DUT1=+0.0, no incorrect (negative) leapsecond will be inferred. (something
179
- that should remain true for the next few centuries, until the length of the day
180
- is 100ms different from 86400 seconds)
181
-
182
-
183
- # The phase modulation channel
184
-
185
- This should be considered more experimental than the AM channel, as the
186
- tests only cover a single reference minute. Further tests could be informed
187
- by the [other implementation I know of](http://www.leapsecond.com/tools/wwvb_pm.c), except that implementation appears incomplete.
188
-
189
-
190
- # Testing wwvbpy
191
-
192
- Run the testsuite, check coverage & type annotations with `gmake`.
193
-
194
- There are several test suites:
195
- * `testwwvb.py`: Check output against expected values. Uses hard coded leap seconds. Tests amplitude and phase data, though the phase testcases are dubious as they were also generated by wwvbpy.
196
- * `testuwwvb.py`: Test the reduced-functionality version against the main version
197
- * `testls.py`: Check the IERS data through 2020-1-1 for expected leap seconds
198
- * `testpm.py`: Check the phase modulation data against a test case from NIST documentation
199
- * `testcli.py`: Check the commandline programs work as expected (limited tests to get 100% coverage)
@@ -1,23 +0,0 @@
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,,
@@ -1,8 +0,0 @@
1
- [console_scripts]
2
- dut1table = wwvb.dut1table:main
3
- updateiers = wwvb.updateiers:main
4
- wwvbdecode = wwvb.decode:main
5
- wwvbgen = wwvb.gen:main
6
-
7
- [gui_scripts]
8
- wwvbtk = wwvb.wwvbtk:main
@@ -1,2 +0,0 @@
1
- uwwvb
2
- wwvb
File without changes
File without changes