fix: update timestamp decoding for Waveform and Continuous records in models and client

This commit is contained in:
Brian Harrison
2026-04-04 00:09:55 -04:00
parent 2286d2ccf8
commit 1c570b083a
3 changed files with 77 additions and 12 deletions

View File

@@ -98,14 +98,17 @@ class Timestamp:
Wire layout (✅ CONFIRMED 2026-04-01 against Blastware event report):
byte[0]: day (uint8)
byte[1]: sub_code / mode flag (0x10 = Waveform mode) 🔶
byte[1]: sub_code / mode flag (0x10 = Waveform single-shot)
byte[2]: month (uint8)
bytes[34]: year (big-endian uint16)
byte[5]: unknown (0x00 in all observed samples)
byte[5]: unknown (0x00 in all observed samples)
byte[6]: hour (uint8)
byte[7]: minute (uint8)
byte[8]: second (uint8)
Used for sub_code=0x10 records only. For sub_code=0x03 (continuous
mode) use from_continuous_record() — the layout is shifted by 1 byte.
Args:
data: at least 9 bytes; only the first 9 are consumed.
@@ -120,7 +123,7 @@ class Timestamp:
f"Waveform record timestamp requires at least 9 bytes, got {len(data)}"
)
day = data[0]
sub_code = data[1] # 0x10 = Waveform; histogram code not yet confirmed
sub_code = data[1] # 0x10 = Waveform single-shot
month = data[2]
year = struct.unpack_from(">H", data, 3)[0]
unknown_byte = data[5]
@@ -139,6 +142,64 @@ class Timestamp:
second=second,
)
@classmethod
def from_continuous_record(cls, data: bytes) -> "Timestamp":
"""
Decode a 10-byte timestamp from the first bytes of a sub_code=0x03
(Waveform Continuous) 210-byte record.
Wire layout (✅ CONFIRMED 2026-04-03 against Blastware event report,
event recorded at 15:20:17 April 3 2026, raw: 10 03 10 04 07 ea 00 0f 14 11):
byte[0]: unknown_a (0x10 observed — meaning TBD)
byte[1]: day (uint8)
byte[2]: unknown_b (0x10 observed — meaning TBD)
bytes[3]: month (uint8)
bytes[45]: year (big-endian uint16)
byte[6]: unknown (0x00 in all observed samples)
byte[7]: hour (uint8)
byte[8]: minute (uint8)
byte[9]: second (uint8)
This is the sub_code=0x10 layout shifted forward by 1 byte, with two
extra unknown bytes at [0] and [2]. The sub_code (0x03) itself is at
byte[1] in the raw record, which also encodes the day — but the day
value (3 = April 3rd) happens to differ from the sub_code (0x03) only
in semantics; the byte is shared.
Args:
data: at least 10 bytes; only the first 10 are consumed.
Returns:
Decoded Timestamp with hour/minute/second populated.
Raises:
ValueError: if data is fewer than 10 bytes.
"""
if len(data) < 10:
raise ValueError(
f"Continuous record timestamp requires at least 10 bytes, got {len(data)}"
)
unknown_a = data[0] # 0x10 observed; meaning unknown
day = data[1] # doubles as the sub_code byte (0x03) — day=3 on Apr 3
unknown_b = data[2] # 0x10 observed; meaning unknown
month = data[3]
year = struct.unpack_from(">H", data, 4)[0]
unknown_byte = data[6]
hour = data[7]
minute = data[8]
second = data[9]
return cls(
raw=bytes(data[:10]),
flag=unknown_a,
year=year,
unknown_byte=unknown_byte,
month=month,
day=day,
hour=hour,
minute=minute,
second=second,
)
@property
def clock_set(self) -> bool:
"""False when year == 1995 (factory default / battery-lost state)."""