diff --git a/src/teletext/models.py b/src/teletext/models.py index 72ef6d8..5cb7c48 100644 --- a/src/teletext/models.py +++ b/src/teletext/models.py @@ -77,18 +77,20 @@ class Page: def calculate_crc(self) -> int: """ - Calculates the CRC-16 (CCITT) checksum for the page. - According to ETSI EN 300 706 (Section 9.4.1.2 & Figure 13): - - Covers Row 0 columns 8-31 (Bytes 14-37 in packet, excluding header and clock). - - Covers Rows 1-25 columns 0-39 (Bytes 6-45 in packet). - - Total 1024 bytes (8192 bits). - - Data is 7-bit (stripped parity). + Calculates the CRC-16 checksum for the page. + According to ETSI EN 300 706 (Section 9.6.1 & Figure 13): + - G(x) = x^16 + x^12 + x^9 + x^7 + 1 (Poly 0x1281) - Initial value: 0. + - Processed bits b8 to b1 (MSB first for stored bytes). + - Total 1024 bytes (32 packets * 32 bytes). + - Packet X/0: Bytes 14 to 37 (24 bytes) + 8 spaces. + - Packets X/1 to X/25: Bytes 14 to 45 (32 bytes). + - Packets X/26 to X/31: 32 spaces each. """ crc = 0 - poly = 0x1021 + poly = 0x1281 - # Helper to update CRC with a byte + # Helper to update CRC with a byte (MSB first) def update_crc(c, val): v = (val << 8) & 0xFFFF for _ in range(8): @@ -101,32 +103,38 @@ class Page: return c # Organize packets by row - rows = {} - for p in self.packets: - rows[p.row] = p + rows = {p.row: p for p in self.packets} + + for r in range(32): # Process 32 slots (0-31) + start, end = 0, 0 + padding = 0 - for r in range(26): # 0 to 25 - # Determine column range if r == 0: - start_col, end_col = 8, 32 # Cols 8-31 (24 bytes) + # Row 0: Bytes 14-37 (24 bytes) + start, end = 8, 32 # data[8..31] + padding = 8 + elif 1 <= r <= 25: + # Rows 1-25: Bytes 14-45 (32 bytes) + start, end = 8, 40 # data[8..39] + padding = 0 else: - start_col, end_col = 0, 40 # Cols 0-39 (40 bytes) + # Rows 26-31: 32 spaces each + padding = 32 - if r in rows: - data = rows[r].data - # Ensure data is long enough (should be 40) - d_len = len(data) - - for i in range(start_col, end_col): - if i < d_len: - byte_val = data[i] & 0x7F # Strip parity - else: - byte_val = 0x20 # Pad with space if short + # Process packet data if available + if r in rows and start < end: + p_data = rows[r].data + for i in range(start, end): + byte_val = (p_data[i] & 0x7F) if i < len(p_data) else 0x20 crc = update_crc(crc, byte_val) - else: - # Missing row treated as spaces - for i in range(start_col, end_col): + elif start < end: + # Missing packet but slot exists + for _ in range(start, end): crc = update_crc(crc, 0x20) + + # Add padding for this slot + for _ in range(padding): + crc = update_crc(crc, 0x20) return crc @@ -140,15 +148,16 @@ class Page: if p.row == 27: # Check Designation Code (Byte 0) try: - b0 = p.data[0] - # Decode Hamming 8/4 - designation = decode_hamming_8_4(b0) - - if designation == 0: - # Packet 27/0 - # Checksum is in bytes 38 and 39. - # ETSI EN 300 706: 8 bits of each byte are used. - if len(p.data) >= 40: + if len(p.data) >= 40: + b0 = p.data[0] + # Decode Hamming 8/4 + designation = decode_hamming_8_4(b0) + + # Packets X/27/0 to X/27/3 exist, but only X/27/0 has the CRC. + # We also check if b0 is raw 0 as a fallback for some captures. + if designation == 0 or b0 == 0: + # Packet 27/0 + # Checksum is in bytes 38 and 39 (TBytes 44 and 45). hi = p.data[38] lo = p.data[39]