fix: update timestamp decoding for Waveform and Continuous records in models and client
This commit is contained in:
@@ -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[3–4]: 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[4–5]: 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)."""
|
||||
|
||||
Reference in New Issue
Block a user