Skip to content
This repository was archived by the owner on Sep 8, 2025. It is now read-only.

Commit c9a0b64

Browse files
authored
Merge pull request #140 from jepler/more-error-detection
2 parents 497164e + ec68ed2 commit c9a0b64

File tree

2 files changed

+44
-9
lines changed

2 files changed

+44
-9
lines changed

src/wwvb/__init__.py

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,6 @@
2424
T = TypeVar("T")
2525

2626

27-
def _require(x: T | None) -> T:
28-
"""Check an Optional item is not None."""
29-
assert x is not None
30-
return x
31-
32-
3327
def _removeprefix(s: str, p: str) -> str:
3428
if s.startswith(p):
3529
return s[len(p) :]
@@ -689,7 +683,7 @@ def from_datetime(
689683
return cls(u.tm_year, u.tm_yday, u.tm_hour, u.tm_min, ut1=newut1, ls=newls)
690684

691685
@classmethod
692-
def from_timecode_am(cls, t: WWVBTimecode) -> WWVBMinute | None:
686+
def from_timecode_am(cls, t: WWVBTimecode) -> WWVBMinute | None: # noqa: PLR0912
693687
"""Construct a WWVBMinute from a WWVBTimecode"""
694688
for i in (0, 9, 19, 29, 39, 49, 59):
695689
if t.am[i] != AmplitudeModulation.MARK:
@@ -704,9 +698,13 @@ def from_timecode_am(cls, t: WWVBTimecode) -> WWVBMinute | None:
704698
minute = t._get_am_bcd(1, 2, 3, 5, 6, 7, 8)
705699
if minute is None:
706700
return None
701+
if minute >= 60:
702+
return None
707703
hour = t._get_am_bcd(12, 13, 15, 16, 17, 18)
708704
if hour is None:
709705
return None
706+
if hour >= 24:
707+
return None
710708
days = t._get_am_bcd(22, 23, 25, 26, 27, 28, 30, 31, 32, 33)
711709
if days is None:
712710
return None
@@ -723,7 +721,9 @@ def from_timecode_am(cls, t: WWVBTimecode) -> WWVBMinute | None:
723721
if days > 366 or (not ly and days > 365):
724722
return None
725723
ls = bool(t.am[56])
726-
dst = _require(t._get_am_bcd(57, 58))
724+
dst = t._get_am_bcd(57, 58)
725+
if dst is None:
726+
return None
727727
return cls(year, days, hour, minute, dst, ut1, ls, ly)
728728

729729

@@ -784,7 +784,10 @@ def _get_am_bcd(self, *poslist: int) -> int | None:
784784
The the bits ``self.am[poslist[i]]`` in MSB order are converted from
785785
BCD to integer
786786
"""
787-
pos = reversed(poslist)
787+
pos = list(poslist)[::-1]
788+
for p in pos:
789+
if self.am[p] not in {AmplitudeModulation.ZERO, AmplitudeModulation.ONE}:
790+
return None
788791
val = [bool(self.am[p]) for p in pos]
789792
result = 0
790793
base = 1

test/testwwvb.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,38 @@ def test_epoch2(self) -> None:
395395
self.assertEqual(WWVBMinute2k(2070, 1, 1, 0, 0).year, 2070)
396396
self.assertEqual(WWVBMinute2k(2099, 1, 1, 0, 0).year, 2099)
397397

398+
def test_invalid_minute(self) -> None:
399+
"""Check that minute 61 is not valid in an AM timecode"""
400+
base_minute = wwvb.WWVBMinute(2021, 1, 1, 0, 0)
401+
minute = base_minute.as_timecode()
402+
minute._put_am_bcd(61, 1, 2, 3, 5, 6, 7, 8) # valid BCD, invalid minute
403+
decoded_minute = wwvb.WWVBMinute.from_timecode_am(minute)
404+
assert decoded_minute is None
405+
406+
def test_invalid_hour(self) -> None:
407+
"""Check that hour 25 is not valid in an AM timecode"""
408+
base_minute = wwvb.WWVBMinute(2021, 1, 1, 0, 0)
409+
minute = base_minute.as_timecode()
410+
minute._put_am_bcd(29, 12, 13, 15, 16, 17, 18) # valid BCD, invalid hour
411+
decoded_minute = wwvb.WWVBMinute.from_timecode_am(minute)
412+
assert decoded_minute is None
413+
414+
def test_invalid_bcd_day(self) -> None:
415+
"""Check that invalid BCD is detected in AM timecode"""
416+
base_minute = wwvb.WWVBMinute(2021, 1, 1, 0, 0)
417+
minute = base_minute.as_timecode()
418+
minute.am[30:34] = [wwvb.AmplitudeModulation.ONE] * 4 # invalid BCD 0xf
419+
decoded_minute = wwvb.WWVBMinute.from_timecode_am(minute)
420+
assert decoded_minute is None
421+
422+
def test_invalid_mark(self) -> None:
423+
"""Check that invalid presence of MARK in a data field is detected"""
424+
base_minute = wwvb.WWVBMinute(2021, 1, 1, 0, 0)
425+
minute = base_minute.as_timecode()
426+
minute.am[57] = wwvb.AmplitudeModulation.MARK
427+
decoded_minute = wwvb.WWVBMinute.from_timecode_am(minute)
428+
assert decoded_minute is None
429+
398430

399431
if __name__ == "__main__":
400432
unittest.main()

0 commit comments

Comments
 (0)