在實作 IDD Features 時,咱們在 Config 類別裡新增一個成員變數 is_e2e_protection_supported,它表示 IDS 伺服器是否支援 E2E-Protection,今天本喵就來說明它是什麼。
E2E-Protection (End-to-End) 是利用計數器和 CRC 來保證資料的完整性和明確性,由 IDD Characteristic 的 Flags 欄位的 bit 0 來指示系統是否支援。E2E-Protection 基本是「全有或全無」的設定,除非特別說明,否則一啟用,所有 characteristics 都必須支援;若關閉,所有 characteristics 也必須移除其相關欄位。
E2E-Counter 的特性如下:
-
(上次接收的 E2E-Counter mod
255
) = 1
由以上描述,咱們應該可以很清楚如何實作:
class Counter:
def __init__(self, init_value):
self.value = init_value
self._init_value = init_value
def reset(self):
self.value = self._init_value
def inc_counter(self):
counter = self.value + 1
self.value = (counter >> 8) + (counter & 0xFF)
class TxCounter(Counter):
def __init__(self):
super().__init__(1)
class RxCounter(Counter):
def __init__(self):
super().__init__(255)
def check(self, received_counter: int) -> bool:
return received_counter - (self.value % 255) == 1
E2E-CRC 是使用 CRC-CCITT,其生成多項式是 g(D) = D^16 + D^12 + D^5 + 1
,即 0x1021,CRC 初始值是 0xFFFF。依照此定義所實作的程式如下:
def add_int8(self, data: int):
for bit in range(8):
# 目前要處理的位元
in_bit = (data >> bit) & 1
msb = (self.crc >> 15) & 1
self.crc <<= 1
if in_bit ^ msb:
self.crc ^= 0x1021
self.crc &= 0xFFFF
不過這還不完整,因為 IDS 規格書要求算出的 CRC 必須將位元由高到低顛倒,也就是:
b15, b14, b13, ... b2, b1, b0
b0, b1, b2, ... b13, b14, b15
為了省下顛倒的動作,咱們將生成多項式先顛倒:
def add_int8(self, data: int):
for bit in range(8):
# 目前要處理的位元
in_bit = (data >> bit) & 1
msb = self.crc & 1
self.crc >>= 1
if in_bit ^ msb:
# 0x8408 是將 0x1021 顛倒後的值
self.crc ^= 0x8408
有 CRC 相關程式經驗的看官說不定會說:「既然那麼在乎效率,怎不使用查表法,還要一個一個位元計算呢?」
看官說的極是啊!
其實本喵一開始也是使用查表法的,但好景不長 ...
不久就遇到了記憶體不足、碎片化造成的怪現象,本喵只好想方設法省記憶體,最後屠刀就動到 CRC 上了。゚ヽ(゚´Д
)ノ゚。`
這裡額外說一下查表法大致會消耗多少記憶體,若咱們用 tuple 來儲存:
要超過 2 KB 的空間來儲存表格 ... 這 ... 實在太多了 ... 本喵投降
(。-_-。)
欲知如何與 IDD Features 整合,且聽下回分曉~