4.5 KiB
CRC при сдвиге битов — симуляция (воспроизведение и оценка сжатия)
Скрипт:
crc_shift_sim.py(рядом).crc8точно как вIR_config.cpp(MSB-first, init=0xFF, без final-xor).poly1=0x31,poly2=0x8C(битовое зеркало 0x31). Модель ошибки: сдвиг битов в СЕРЕДИНЕ данных (бит-слип / циклический сдвиг), длина кадра сохраняется; «чек» сверяется какcheck(искажённые данные) == check(оригинал). 200000 попыток на модель, данные 3/5/8 байт.
1. Твой исторический баг ВОСПРОИЗВЕДён
Одиночный CRC8 (один полином) реально пропускает сдвиг в середине пакета. Примеры (данные → сдвиг → искажённые):
26 0b 05→26 16 0a:crc1=0x78совпал → single ПРОШЁЛ; двойная схема:crc2 0xd8≠0x5c→ поймала.cc4fb8f633→cc5f71ec66:crc1=0x20совпал → single прошёл; double поймала (crc2 0xfc≠0x90).b983315b416440c3→b983315e82c88186:crc1=0x3aсовпал → single прошёл; double поймала.
2. Частота пропуска (доля НЕобнаруженных сдвигов)
| Схема | slip-delete | rotate-all | rotate-mid-window |
|---|---|---|---|
| single (1×CRC8, 8 бит) | 0.29–0.41 % | ~0.78 % | 0.55–0.78 % |
| double (2×CRC8, 16 бит) — текущая | 0.007–0.015 % | 0.025–0.03 % | 0.008–0.026 % |
| nibble-fold (4+4 бит) | 0.76–0.80 % | ~1.58 % | 1.18–1.55 % |
| nibble-trunc (4+4 бит) | 1.37–1.57 % | ~1.53 % | 1.22–1.57 % |
Вывод: double примерно в 20–40× надёжнее single и ловит практически все сдвиги, которые single пропускает. Это подтверждает, зачем добавляли второй полином (0x8C = зеркало 0x31 ловит «зеркальные» сдвиговые ошибки, слепые для 0x31).
3. Идея «склеить 2 байта в 1» (полбайта на полином) — ХУЖЕ, чем есть
- nibble-fold (свернуть каждый CRC8 в 4 бита xor'ом) и nibble-trunc (взять по 4 бита) дают 0.6–1.6 % пропусков — это ХУЖЕ даже одиночного полного CRC8 и в ~50–150× хуже текущей двойной схемы.
- Burst-ошибки (подряд искажённые биты — типичный IR-сбой: бит-слипы/всплески). Гарантия обнаружения = ширине контроля:
- single 8 бит и double — надёжны на коротких burst (≥8 бит), эмпирически в выборке не пропускали и длиннее;
- nibble-fold — гарантия ~4 бита (эмпирически до 7);
- nibble-trunc — пропускает даже ОДИНОЧНЫЙ бит (усечение теряет старший ниббл) → так делать нельзя.
4. Рекомендация
- CRC — не то место, где стоит экономить байт. Сжатие контроля до 1 байта повышает пропуск сдвигов/burst в ~50–150× (с ~0.01 % до ~1 %) — прямо в том классе ошибок, ради которого второй полином и вводили.
- Если байт очень нужен — забирать его не из CRC, а из уже выявленного резерва: свободные
msgType(3,5), «пустая» зона длины (компактные кадры), или переупаковка адресов/полей. 16-битный контроль (2×CRC8) сохранить. - Если всё же сжимать CRC до 1 байта — только fold (xor нибблов), никогда не truncate; и принять ~1 % пропуска на сдвигах (в ~50–100× хуже текущего). Как отдельный компромисс — обсуждать вместе с обратной совместимостью.
(Обратную совместимость версий протокола обсудим отдельно — по запросу Даши.)